1 (*
2 * Hedgewars, a free turn based strategy game
3 * Copyright (c) 2004-2015 Andrey Korotaev <unC0Rr@gmail.com>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; version 2 of the License
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
17 *)
18
19 {$INCLUDE "options.inc"}
20
21 unit uStats;
22 interface
23 uses uConsts, uTypes;
24
25 var TotalRoundsPre: LongInt; // Helper variable for calculating start of Sudden Death and more. Starts at -1 and is incremented on the turn BEFORE the turn which marks the start of the next round. Always -1 while in hog placing phase
26 TotalRoundsReal: LongInt; // Total number of rounds played (-1 if not started or in hog placing phase). Exported to Lua as 'TotalRounds'
27 FinishedTurnsTotal: LongInt;
28 // Variables to disable certain portions of game stats (set by Lua)
29 SendGameResultOn : boolean = true;
30 SendRankingStatsOn : boolean = true;
31 SendAchievementsStatsOn : boolean = true;
32 SendHealthStatsOn : boolean = true;
33 // Clan death log, used for game stats
34 ClanDeathLog : PClanDeathLogEntry;
35
36 procedure initModule;
37 procedure freeModule;
38
39 procedure AmmoUsed(am: TAmmoType);
40 procedure HedgehogPoisoned(Gear: PGear; Attacker: PHedgehog);
41 procedure HedgehogSacrificed(Hedgehog: PHedgehog);
42 procedure HedgehogDamaged(Gear: PGear; Attacker: PHedgehog; Damage: Longword; killed: boolean);
43 procedure TargetHit;
44 procedure Skipped;
getIsTurnSkippednull45 function getIsTurnSkipped: boolean;
46 procedure TurnStats;
47 procedure TurnReaction;
48 procedure TurnStatsReset;
49 procedure SendStats;
50 procedure hedgehogFlight(Gear: PGear; time: Longword);
51 procedure declareAchievement(id, teamname, location: shortstring; value: LongInt);
52 procedure startGhostPoints(n: LongInt);
53 procedure dumpPoint(x, y: LongInt);
54
55 implementation
56 uses uSound, uLocale, uVariables, uUtils, uIO, uCaptions, uMisc, uConsole, uScript;
57
58 var DamageClan : Longword = 0; // Damage of own clan in turn
59 DamageTeam : Longword = 0; // Damage of own team in turn
60 DamageTotal : Longword = 0; // Total damage dealt in game
61 DamageTurn : Longword = 0; // Damage in turn
62 PoisonTurn : Longword = 0; // Poisoned enemies in turn
63 PoisonClan : Longword = 0; // Poisoned own clan members in turn
64 PoisonTeam : Longword = 0; // Poisoned own team members in turn
65 PoisonTotal : Longword = 0; // Poisoned hogs in whole round
66 KillsClan : LongWord = 0; // Own clan members killed in turn
67 KillsTeam : LongWord = 0; // Own team members killed in turn
68 KillsSD : LongWord = 0; // Killed hedgehogs in turn that died by Sudden Death water rise
69 Kills : LongWord = 0; // Killed hedgehogs in turn (including those that died by Sudden Death water rise)
70 KillsTotal : LongWord = 0; // Total killed hedgehogs in game
71 HitTargets : LongWord = 0; // Target (gtTarget) hits in turn
72 AmmoUsedCount : Longword = 0; // Number of times an ammo has been used this turn
73 AmmoDamagingUsed : boolean = false; // true if damaging ammo was used in turn
74 FirstBlood : boolean = false; // true if the “First blood” taunt has been used in this game
75 StepFirstBlood : boolean = false; // true if the “First blood” taunt is to be used this turn
76 LeaveMeAlone : boolean = false; // true if the “Leave me alone” taunt is to be used this turn
77 SkippedTurns: LongWord = 0; // number of skipped turns in game
78 isTurnSkipped: boolean = false; // true if this turn was skipped
79 vpHurtSameClan: PVoicepack = nil; // voicepack of current clan (used for taunts)
80 vpHurtEnemy: PVoicepack = nil; // voicepack of enemy (used for taunts)
81
82 procedure HedgehogPoisoned(Gear: PGear; Attacker: PHedgehog);
83 begin
84 if Attacker^.Team^.Clan = Gear^.Hedgehog^.Team^.Clan then
85 begin
86 vpHurtSameClan:= Gear^.Hedgehog^.Team^.voicepack;
87 inc(PoisonClan);
88 if Attacker^.Team = Gear^.Hedgehog^.Team then
89 inc(PoisonTeam);
90 end
91 else
92 begin
93 if not FirstBlood then
94 StepFirstBlood:= true;
95 vpHurtEnemy:= Gear^.Hedgehog^.Team^.voicepack;
96 inc(PoisonTurn)
97 end;
98 Gear^.Hedgehog^.stats.StepPoisoned:= true;
99 inc(PoisonTotal)
100 end;
101
102 procedure HedgehogSacrificed(Hedgehog: PHedgehog);
103 begin
104 Hedgehog^.stats.Sacrificed:= true
105 end;
106
107 procedure HedgehogDamaged(Gear: PGear; Attacker: PHedgehog; Damage: Longword; killed: boolean);
108 begin
109 if Attacker^.Team^.Clan = Gear^.Hedgehog^.Team^.Clan then
110 vpHurtSameClan:= Gear^.Hedgehog^.Team^.voicepack
111 else
112 begin
113 if not FirstBlood then
114 StepFirstBlood:= true;
115 vpHurtEnemy:= Gear^.Hedgehog^.Team^.voicepack;
116 if (not killed) and (not bDuringWaterRise) then
117 begin
118 // Check if victim got attacked by RevengeHog again
119 if (Gear^.Hedgehog^.RevengeHog <> nil) and (Gear^.Hedgehog^.RevengeHog = Attacker) and (Gear^.Hedgehog^.stats.StepRevenge = false) then
120 LeaveMeAlone:= true;
121 // Check if attacker got revenge
122 if (Attacker^.RevengeHog <> nil) and (Attacker^.RevengeHog = Gear^.Hedgehog) then
123 begin
124 Attacker^.stats.GotRevenge:= true;
125 // Also reset the "in-row" counter to restore LeaveMeAlone/CutItOut taunts
126 Attacker^.stats.StepDamageRecvInRow:= 0;
127 Attacker^.RevengeHog:= nil;
128 end
129 // If not, victim remembers their attacker to plan *their* revenge
130 else
131 begin
132 Gear^.Hedgehog^.RevengeHog:= Attacker;
133 // To prevent "LeaveMeAlone" being activated if same hog is hit by attacker
134 // multiple times in the same turn.
135 Gear^.Hedgehog^.stats.StepRevenge:= true;
136 end;
137 end
138 end;
139
140 //////////////////////////
141
142 if (not bDuringWaterRise) then
143 begin
144 inc(Attacker^.stats.StepDamageGiven, Damage);
145 inc(Gear^.Hedgehog^.stats.StepDamageRecv, Damage);
146 end;
147
148 if CurrentHedgehog^.Team^.Clan = Gear^.Hedgehog^.Team^.Clan then inc(DamageClan, Damage);
149 if CurrentHedgehog^.Team = Gear^.Hedgehog^.Team then inc(DamageTeam, Damage);
150
151 if killed then
152 begin
153 Gear^.Hedgehog^.stats.StepDied:= true;
154 inc(Kills);
155
156 inc(KillsTotal);
157
158 if bDuringWaterRise then
159 inc(KillsSD)
160 else
161 begin
162 inc(Attacker^.stats.StepKills);
163 inc(Attacker^.Team^.stats.Kills);
164 if (Attacker^.Team^.TeamName = Gear^.Hedgehog^.Team^.TeamName) then
165 begin
166 inc(Attacker^.Team^.stats.TeamKills);
167 inc(Attacker^.Team^.stats.TeamDamage, Gear^.Damage);
168 end;
169 if Gear = Attacker^.Gear then
170 inc(Attacker^.Team^.stats.Suicides);
171 if Attacker^.Team^.Clan = Gear^.Hedgehog^.Team^.Clan then
172 begin
173 inc(KillsClan);
174 if Attacker^.Team = Gear^.Hedgehog^.Team then
175 inc(KillsTeam);
176 end;
177 end;
178 end;
179
180 inc(DamageTotal, Damage);
181 inc(DamageTurn, Damage)
182 end;
183
184 procedure TargetHit();
185 begin
186 inc(HitTargets)
187 end;
188
189 procedure Skipped;
190 begin
191 inc(SkippedTurns);
192 isTurnSkipped:= true
193 end;
194
getIsTurnSkippednull195 function getIsTurnSkipped: boolean;
196 begin
197 getIsTurnSkipped:= isTurnSkipped;
198 end;
199
200 procedure TurnStats;
201 var i, t: LongInt;
202 c: Longword;
203 newEntry: PClanDeathLogEntry;
204 begin
205 inc(FinishedTurnsTotal);
206
207 for t:= 0 to Pred(TeamsCount) do // send even on zero turn
208 with TeamsArray[t]^ do
209 for i:= 0 to cMaxHHIndex do
210 begin
211 with Hedgehogs[i].stats do
212 begin
213 inc(DamageRecv, StepDamageRecv);
214 inc(DamageGiven, StepDamageGiven);
215 if StepDamageRecv > MaxStepDamageRecv then
216 MaxStepDamageRecv:= StepDamageRecv;
217 if StepDamageGiven > MaxStepDamageGiven then
218 MaxStepDamageGiven:= StepDamageGiven;
219 if StepKills > MaxStepKills then
220 MaxStepKills:= StepKills;
221 if (Hedgehogs[i].Team <> nil) and (Hedgehogs[i].Team^.Clan^.ClanIndex <> CurrentHedgehog^.Team^.Clan^.ClanIndex) then
222 begin
223 if StepDamageRecv > 0 then
224 inc(StepDamageRecvInRow)
225 else
226 StepDamageRecvInRow:= 0;
227 if StepDamageRecvInRow >= 3 then
228 LeaveMeAlone:= true;
229 end;
230 end;
231 end;
232
233
234 // Write into the death log which clans died in this turn,
235 // important for final rankings.
236 c:= 0;
237 newEntry:= nil;
238 for t:= 0 to Pred(ClansCount) do
239 with ClansArray[t]^ do
240 begin
241 if (ClanHealth = 0) and (ClansArray[t]^.DeathLogged = false) then
242 begin
243 if c = 0 then
244 begin
245 new(newEntry);
246 newEntry^.Turn := FinishedTurnsTotal;
247 newEntry^.NextEntry := nil;
248 end;
249
250 newEntry^.KilledClans[c]:= ClansArray[t];
251 inc(c);
252 newEntry^.KilledClansCount := c;
253 ClansArray[t]^.DeathLogged:= true;
254 end;
255
256 if SendHealthStatsOn then
257 SendStat(siClanHealth, IntToStr(Color) + ' ' + IntToStr(ClanHealth));
258 end;
259 if newEntry <> nil then
260 begin
261 if ClanDeathLog <> nil then
262 begin
263 newEntry^.NextEntry:= ClanDeathLog;
264 end;
265 ClanDeathLog:= newEntry;
266 end;
267
268 end;
269
270 procedure TurnReaction;
271 var killsCheck: LongInt;
272 s: ansistring;
273 begin
274 //TryDo(not bBetweenTurns, 'Engine bug: TurnReaction between turns', true);
275
276 if FinishedTurnsTotal <> 0 then
277 begin
278 s:= ansistring(CurrentHedgehog^.Name);
279 inc(CurrentHedgehog^.stats.FinishedTurns);
280
281 // killsCheck is used to take deaths into account that were not a traditional "kill"
282 // Hogs that died during SD water rise do not count as "kills" for taunts
283 killsCheck:= KillsSD;
284 // If the hog sacrificed (=kamikaze/piano) itself, this needs to be taken into account for the reactions later
285 if (CurrentHedgehog^.stats.Sacrificed) then
286 inc(killsCheck);
287
288 // First blood (first damage, poison or kill of enemy)
289 if (StepFirstBlood) and (not FirstBlood) and (ClansCount > 1) and ((DamageTotal > 0) or (KillsTotal > 0) or (PoisonTotal > 0)) then
290 begin
291 FirstBlood:= true;
292 AddVoice(sndFirstBlood, CurrentTeam^.voicepack);
293 end
294
295 // Hog hurts, poisons or kills itself (except sacrifice)
296 else if (CurrentHedgehog^.stats.Sacrificed = false) and ((CurrentHedgehog^.stats.StepDamageRecv > 0) or (CurrentHedgehog^.stats.StepPoisoned) or (CurrentHedgehog^.stats.StepDied)) then
297 // Hurt itself only (without dying)
298 if (CurrentHedgehog^.stats.StepDamageGiven = CurrentHedgehog^.stats.StepDamageRecv) and (CurrentHedgehog^.stats.StepDamageRecv >= 1) and (not CurrentHedgehog^.stats.StepDied) then
299 begin
300 // Announcer message + random taunt
301 AddCaption(FormatA(GetEventString(eidHurtSelf), s), capcolDefault, capgrpMessage);
302 if (CurrentHedgehog^.stats.StepDamageGiven <= CurrentHedgehog^.stats.StepDamageRecv) and (CurrentHedgehog^.stats.StepDamageRecv >= 1) then
303 case random(3) of
304 0: AddVoice(sndStupid, PreviousTeam^.voicepack);
305 1: AddVoice(sndBugger, CurrentTeam^.voicepack);
306 2: AddVoice(sndDrat, CurrentTeam^.voicepack);
307 end;
308 end
309 // Hurt itself and others, or died
310 else
311 AddVoice(sndStupid, PreviousTeam^.voicepack)
312
313 // Hog hurts, poisons or kills own team/clan member. Sacrifice is taken into account
314 else if (DamageClan <> 0) or (KillsClan > killsCheck) or (PoisonClan <> 0) then
315 if (DamageTurn > DamageClan) or ((Kills-KillsSD) > KillsClan) then
316 if random(2) = 0 then
317 AddVoice(sndNutter, CurrentTeam^.voicepack)
318 else
319 AddVoice(sndWatchIt, vpHurtSameClan)
320 else
321 // Attacked same team
322 if (random(2) = 0) and ((DamageTeam <> 0) or (KillsTeam > killsCheck) or (PoisonTeam <> 0)) then
323 AddVoice(sndSameTeam, vpHurtSameClan)
324 // Attacked same team or a clan member
325 else
326 AddVoice(sndTraitor, vpHurtSameClan)
327
328 // Hog hurts, kills or poisons enemy
329 else if (CurrentHedgehog^.stats.StepDamageGiven <> 0) or (CurrentHedgehog^.stats.StepKills > killsCheck) or (PoisonTurn <> 0) then
330 // 3 kills or more
331 if Kills > killsCheck + 2 then
332 AddVoice(sndAmazing, CurrentTeam^.voicepack)
333 // 2 kills
334 else if Kills = (killsCheck + 2) then
335 if random(2) = 0 then
336 AddVoice(sndBrilliant, CurrentTeam^.voicepack)
337 else
338 AddVoice(sndExcellent, CurrentTeam^.voicepack)
339 // 1 kill
340 else if Kills = (killsCheck + 1) then
341 AddVoice(sndEnemyDown, CurrentTeam^.voicepack)
342 // 0 kills, only damage or poison
343 else
344 // possible reactions of victim, in the order of preference:
345 // 1. claiming revenge
346 // 2. complaining about getting attacked too often
347 // 3. threatening enemy with retaliation
348 if CurrentHedgehog^.stats.GotRevenge then
349 begin
350 AddVoice(sndRevenge, CurrentHedgehog^.Team^.voicepack);
351 // If revenge taunt was added, one of the following voices is
352 // added as fallback (4th param), in case of a missing Revenge sound file.
353 case random(4) of
354 0: AddVoice(sndRegret, vpHurtEnemy, false, true);
355 1: AddVoice(sndGonnaGetYou, vpHurtEnemy, false, true);
356 2: AddVoice(sndIllGetYou, vpHurtEnemy, false, true);
357 3: AddVoice(sndJustYouWait, vpHurtEnemy, false, true);
358 end;
359 end
360 else
361 if LeaveMeAlone then
362 if random(2) = 0 then
363 AddVoice(sndCutItOut, vpHurtEnemy)
364 else
365 AddVoice(sndLeaveMeAlone, vpHurtEnemy)
366 else
367 case random(4) of
368 0: AddVoice(sndRegret, vpHurtEnemy);
369 1: AddVoice(sndGonnaGetYou, vpHurtEnemy);
370 2: AddVoice(sndIllGetYou, vpHurtEnemy);
371 3: AddVoice(sndJustYouWait, vpHurtEnemy);
372 end
373
374 // Missed shot
375 // A miss is defined as a shot with a damaging weapon with 0 kills, 0 damage, 0 hogs poisoned and 0 targets hit
376 else if AmmoDamagingUsed and (Kills <= killsCheck) and (PoisonTurn = 0) and (PoisonClan = 0) and (DamageTurn = 0) and (HitTargets = 0) then
377 // Chance to call hedgehog stupid or nutter if sacrificed for nothing
378 if CurrentHedgehog^.stats.Sacrificed then
379 case random(3) of
380 0: AddVoice(sndMissed, PreviousTeam^.voicepack);
381 1: AddVoice(sndStupid, PreviousTeam^.voicepack);
382 2: AddVoice(sndNutter, PreviousTeam^.voicepack);
383 end
384 else
385 AddVoice(sndMissed, PreviousTeam^.voicepack)
386
387 // Timeout
388 else if (AmmoUsedCount > 0) and (not isTurnSkipped) then
389 begin end// nothing ?
390
391 // Turn skipped
392 else if isTurnSkipped and (not PlacingHogs) and (not PlacingKings) then
393 begin
394 AddVoice(sndCoward, PreviousTeam^.voicepack);
395 AddCaption(FormatA(GetEventString(eidTurnSkipped), s), capcolDefault, capgrpMessage);
396 end
397 end;
398 end;
399
400 procedure TurnStatsReset;
401 var t, i: LongInt;
402 begin
403 for t:= 0 to Pred(TeamsCount) do // send even on zero turn
404 with TeamsArray[t]^ do
405 for i:= 0 to cMaxHHIndex do
406 with Hedgehogs[i].stats do
407 begin
408 StepKills:= 0;
409 StepDamageRecv:= 0;
410 StepDamageGiven:= 0;
411 StepPoisoned:= false;
412 StepDied:= false;
413 GotRevenge:= false;
414 StepRevenge:= false;
415 end;
416
417 Kills:= 0;
418 KillsSD:= 0;
419 KillsClan:= 0;
420 KillsTeam:= 0;
421 DamageClan:= 0;
422 DamageTeam:= 0;
423 DamageTurn:= 0;
424 HitTargets:= 0;
425 PoisonClan:= 0;
426 PoisonTeam:= 0;
427 PoisonTurn:= 0;
428 AmmoUsedCount:= 0;
429 LeaveMeAlone:= false;
430 AmmoDamagingUsed:= false;
431 isTurnSkipped:= false;
432 StepFirstBlood:= false;
433 end;
434
435 procedure AmmoUsed(am: TAmmoType);
436 begin
437 inc(AmmoUsedCount);
438 AmmoDamagingUsed:= AmmoDamagingUsed or Ammoz[am].isDamaging
439 end;
440
441 procedure hedgehogFlight(Gear: PGear; time: Longword);
442 begin
443 if time > 4000 then
444 begin
445 WriteLnToConsole('FLIGHT');
446 WriteLnToConsole(Gear^.Hedgehog^.Team^.TeamName);
447 WriteLnToConsole(inttostr(time));
448 WriteLnToConsole( '');
449 end
450 end;
451
452 procedure SendStats;
453 var i, t, c: LongInt;
454 msd, msk: Longword; msdhh, mskhh: PHedgehog;
455 mskcnt: Longword;
456 maxTeamKills : Longword;
457 maxTeamKillsName : shortstring;
458 maxTurnSkips : Longword;
459 maxTurnSkipsName : shortstring;
460 maxTeamDamage : Longword;
461 maxTeamDamageName : shortstring;
462 winnersClan : PClan;
463 deathEntry : PClanDeathLogEntry;
464 currentRank: Longword;
465 begin
466 if SendHealthStatsOn then
467 msd:= 0; msdhh:= nil;
468 msk:= 0; mskhh:= nil;
469 mskcnt:= 0;
470 maxTeamKills := 0;
471 maxTurnSkips := 0;
472 maxTeamDamage := 0;
473 winnersClan:= nil;
474 currentRank:= 0;
475
476 for t:= 0 to Pred(TeamsCount) do
477 with TeamsArray[t]^ do
478 begin
479 if (not ExtDriven) and SendRankingStatsOn then
480 SendStat(siTeamStats, GetTeamStatString(TeamsArray[t]));
481 for i:= 0 to cMaxHHIndex do
482 begin
483 if Hedgehogs[i].stats.MaxStepDamageGiven > msd then
484 begin
485 msdhh:= @Hedgehogs[i];
486 msd:= Hedgehogs[i].stats.MaxStepDamageGiven
487 end;
488 if Hedgehogs[i].stats.MaxStepKills >= msk then
489 if Hedgehogs[i].stats.MaxStepKills = msk then
490 inc(mskcnt)
491 else
492 begin
493 mskcnt:= 1;
494 mskhh:= @Hedgehogs[i];
495 msk:= Hedgehogs[i].stats.MaxStepKills
496 end;
497 end;
498
499 { Send player stats for winner clans/teams.
500 The clan that survived is ranked 1st. }
501 if (Clan^.ClanHealth > 0) then
502 begin
503 winnersClan:= Clan;
504 if SendRankingStatsOn then
505 begin
506 currentRank:= 1;
507 SendStat(siTeamRank, _S'1');
508 SendStat(siPlayerKills, IntToStr(Clan^.Color) + ' ' +
509 IntToStr(stats.Kills) + ' ' + TeamName);
510 end;
511 end;
512
513 { determine maximum values of TeamKills, TurnSkips, TeamDamage }
514 if stats.TeamKills > maxTeamKills then
515 begin
516 maxTeamKills := stats.TeamKills;
517 maxTeamKillsName := TeamName;
518 end;
519 if stats.TurnSkips > maxTurnSkips then
520 begin
521 maxTurnSkips := stats.TurnSkips;
522 maxTurnSkipsName := TeamName;
523 end;
524 if stats.TeamDamage > maxTeamDamage then
525 begin
526 maxTeamDamage := stats.TeamDamage;
527 maxTeamDamageName := TeamName;
528 end;
529
530 end;
531
532 inc(currentRank);
533
534 { Now send player stats for loser teams/clans.
535 The losing clans are ranked in the reverse order they died.
536 The clan that died last is ranked 2nd,
537 the clan that died second to last is ranked 3rd,
538 and so on.
539 Clans that died in the same turn share their rank.
540 If a clan died multiple times in the match
541 (e.g. due to resurrection), only the *latest* death of
542 that clan counts (handled in gtResurrector).
543 }
544 deathEntry := ClanDeathLog;
545 i:= 0;
546 if SendRankingStatsOn then
547 while (deathEntry <> nil) do
548 begin
549 for c:= 0 to Pred(deathEntry^.KilledClansCount) do
550 if ((deathEntry^.KilledClans[c]^.ClanHealth) = 0) and (not deathEntry^.KilledClans[c]^.StatsHandled) then
551 begin
552 for t:= 0 to Pred(TeamsCount) do
553 if (TeamsArray[t]^.Clan^.ClanIndex = deathEntry^.KilledClans[c]^.ClanIndex) then
554 begin
555 SendStat(siTeamRank, IntToStr(currentRank));
556 SendStat(siPlayerKills, IntToStr(deathEntry^.killedClans[c]^.Color) + ' ' +
557 IntToStr(TeamsArray[t]^.stats.Kills) + ' ' + TeamsArray[t]^.TeamName);
558 end;
559 deathEntry^.KilledClans[c]^.StatsHandled:= true;
560 inc(i);
561 end;
562 if i > 0 then
563 inc(currentRank, i);
564 i:= 0;
565 deathEntry:= deathEntry^.NextEntry;
566 end;
567
568 // "Achievements" / Details part of stats screen
569 if SendAchievementsStatsOn then
570 begin
571 if msdhh <> nil then
572 SendStat(siMaxStepDamage, IntToStr(msd) + ' ' + msdhh^.Name + ' (' + msdhh^.Team^.TeamName + ')');
573 if mskcnt = 1 then
574 SendStat(siMaxStepKills, IntToStr(msk) + ' ' + mskhh^.Name + ' (' + mskhh^.Team^.TeamName + ')');
575
576 if maxTeamKills > 1 then
577 SendStat(siMaxTeamKills, IntToStr(maxTeamKills) + ' ' + maxTeamKillsName);
578 if maxTurnSkips > 2 then
579 SendStat(siMaxTurnSkips, IntToStr(maxTurnSkips) + ' ' + maxTurnSkipsName);
580 if maxTeamDamage > 30 then
581 SendStat(siMaxTeamDamage, IntToStr(maxTeamDamage) + ' ' + maxTeamDamageName);
582
583 if KilledHHs > 0 then
584 SendStat(siKilledHHs, IntToStr(KilledHHs));
585 end;
586
587 // now to console
588 if winnersClan <> nil then
589 begin
590 ScriptCall('onGameResult', winnersClan^.ClanIndex);
591 WriteLnToConsole('WINNERS');
592 WriteLnToConsole(inttostr(winnersClan^.TeamsNumber));
593 for t:= 0 to winnersClan^.TeamsNumber - 1 do
594 WriteLnToConsole(winnersClan^.Teams[t]^.TeamName);
595 end
596 else
597 begin
598 ScriptCall('onGameResult', -1);
599 WriteLnToConsole('DRAW');
600 end;
601
602 ScriptCall('onAchievementsDeclaration');
603 end;
604
605 procedure declareAchievement(id, teamname, location: shortstring; value: LongInt);
606 begin
607 if (length(id) = 0) or (length(teamname) = 0) or (length(location) = 0) then exit;
608 WriteLnToConsole('ACHIEVEMENT');
609 WriteLnToConsole(id);
610 WriteLnToConsole(teamname);
611 WriteLnToConsole(location);
612 WriteLnToConsole(inttostr(value));
613 end;
614
615 procedure startGhostPoints(n: LongInt);
616 begin
617 WriteLnToConsole('GHOST_POINTS');
618 WriteLnToConsole(inttostr(n));
619 end;
620
621 procedure dumpPoint(x, y: LongInt);
622 begin
623 WriteLnToConsole(inttostr(x));
624 WriteLnToConsole(inttostr(y));
625 end;
626
627 procedure initModule;
628 begin
629 DamageClan := 0;
630 DamageTeam := 0;
631 DamageTotal := 0;
632 DamageTurn := 0;
633 PoisonClan := 0;
634 PoisonTeam := 0;
635 PoisonTurn := 0;
636 KillsClan := 0;
637 KillsTeam := 0;
638 KillsSD := 0;
639 Kills := 0;
640 KillsTotal := 0;
641 HitTargets := 0;
642 AmmoUsedCount := 0;
643 AmmoDamagingUsed := false;
644 FirstBlood:= false;
645 StepFirstblood:= false;
646 LeaveMeAlone := false;
647 SkippedTurns:= 0;
648 isTurnSkipped:= false;
649 vpHurtSameClan:= nil;
650 vpHurtEnemy:= nil;
651 TotalRoundsPre:= -1;
652 TotalRoundsReal:= -1;
653 FinishedTurnsTotal:= -1;
654 ClanDeathLog:= nil;
655 end;
656
657 procedure freeModule;
658 begin
659 end;
660
661 end.
662