1 |
#!/bin/perl -W |
#!/bin/perl -W |
2 |
|
|
3 |
|
use strict; |
4 |
use GD; |
use GD; |
5 |
|
|
6 |
|
die "Usage: t2matchlog logfile outputdir" unless @ARGV == 2; |
7 |
|
open LOG, $ARGV[0] or die "Failed to open $ARGV[0]: $!"; |
8 |
|
my $outdir = $ARGV[1]; |
9 |
|
mkdir $outdir or die "Failed to create output directory $outdir: $!"; |
10 |
|
(system "cp t2matchlog.css $outdir/") == 0 or die "Failed to copy stylesheet: $!"; |
11 |
|
|
12 |
$/ = "\r\n"; |
$/ = "\r\n"; |
13 |
|
$| = 1; |
14 |
|
|
15 |
$near0 = 70; |
my $near0 = 70; |
16 |
$near1 = 200; |
my $near1 = 200; |
17 |
$thumbsize = 400; |
my $thumbsize = 400; |
|
|
|
|
$black = 0x000000; |
|
|
$white = 0xffffff; |
|
|
$red = 0xff0000; |
|
|
$green = 0x00ff00; |
|
|
$blue = 0x0000ff; |
|
|
$magenta = 0xff00ff; |
|
|
$yellow = 0xffff00; |
|
|
$orange = 0xff8000; |
|
|
$grey = 0x404040; |
|
|
$teamcol[1] = $blue; |
|
|
$teamcol[2] = $magenta; |
|
18 |
|
|
19 |
# from damageTypes.cs |
# from damageTypes.cs |
20 |
|
my @DamageTypeText; |
21 |
$DamageTypeText[0] = 'default'; |
$DamageTypeText[0] = 'default'; |
22 |
$DamageTypeText[1] = 'blaster'; |
$DamageTypeText[1] = 'blaster'; |
23 |
$DamageTypeText[2] = 'plasma'; |
$DamageTypeText[2] = 'plasma'; |
57 |
$DamageTypeText[98] = 'nexus camping'; |
$DamageTypeText[98] = 'nexus camping'; |
58 |
$DamageTypeText[99] = 'suicide'; |
$DamageTypeText[99] = 'suicide'; |
59 |
|
|
60 |
$icon_player = load_image("icons/com_player_grey_24x.png"); |
my $icon_player = load_image("icons/com_player_grey_24x.png"); |
61 |
|
my %icon; |
62 |
$icon{'flag'} = load_image("icons/com_icon_flag_outside.png"); |
$icon{'flag'} = load_image("icons/com_icon_flag_outside.png"); |
63 |
$icon{'gen'} = load_image("icons/com_icon_generator.png"); |
$icon{'gen'} = load_image("icons/com_icon_generator.png"); |
64 |
$icon{'turret'} = load_image("icons/com_icon_turretbase.png"); |
$icon{'turret'} = load_image("icons/com_icon_turretbase.png"); |
65 |
$icon{'inv'} = load_image("icons/com_icon_inventory.png"); |
$icon{'inv'} = load_image("icons/com_icon_inventory.png"); |
66 |
$icon{'sensor'} = load_image("icons/com_icon_sensor.png"); |
$icon{'sensor'} = load_image("icons/com_icon_sensor.png"); |
67 |
$icon{'vpad'} = load_image("icons/com_icon_vehicle_inventory.png"); |
$icon{'vpad'} = load_image("icons/com_icon_vehicle_inventory.png"); |
68 |
|
$icon{'solar'} = load_image("icons/com_icon_solar_gen.png"); |
69 |
|
|
70 |
# read header |
my %colour = ('S' => 0xffff00, 'A' => 0xff8000, 'KK' => 0x00ff00, 'K' => 0xff0000, |
71 |
$mission = <>; chomp $mission; |
'L' => 0xff8000, 'F+' => 0x00ffff, 'F-' => 0xff00ff, 'P' => 0xff8000, |
72 |
$mission_type = <>; chomp $mission_type; |
'V' => 0xffff00, 'W' => 0xff0000, 'G' => 0x0080ff, 'J' => 0x00ff80, |
73 |
$mission_name = <>; chomp $mission_name; |
'GG' => 0x0080ff, 'JJ' => 0x00ff80); |
74 |
$mission_type_name = <>; chomp $mission_type_name; |
my $colour_me = 0xffffff; |
75 |
$area = <>; chomp $area; ($x0, $y0, $width, $height) = split / /, $area; |
my %desc = ('S' => 'spawned on team $team_name[$p1]', |
76 |
while (($_ = <>) ne $/) { |
'A' => '$p1 armour', |
77 |
chomp; |
'KK' => 'killed $enemy using $DamageTypeText[$p2]', |
78 |
($team, $type, $block, $name, $x, $y, $z, $desc) = split / /, $_, 8; |
'K' => 'killed by $DamageTypeText[$p2] $enemy', |
79 |
$x -= $x0; $y = $height - $y + $y0; |
'L' => 'left the game', |
80 |
if ($type eq 'StaticShape' and $block eq 'GeneratorLarge') { |
'F+' => 'took the $team_name[$p1] flag', |
81 |
push @landmarks, "$z gen $team $x $y"; |
'F-' => 'dropped the $team_name[$p1] flag', |
82 |
} elsif ($type eq 'Turret' and $block eq 'TurretBaseLarge') { |
'P' => '$p1', |
83 |
push @landmarks, "$z turret $team $x $y"; |
'V' => 'vehicle created', |
84 |
} elsif ($type eq 'StaticShape' and $block eq 'ExteriorFlagStand') { |
'W' => 'vehicle destroyed', |
85 |
push @landmarks, "$z flag $team $x $y"; |
'G' => 'entered " . vehicle_link($p1) . " as ${seat{$p2}}', |
86 |
} elsif ($type eq 'StaticShape' and $block eq 'StationVehicle') { |
'J' => 'ejected from vehicle', |
87 |
push @landmarks, "$z vpad $team $x $y"; |
'GG' => '<a href=\'" . safe_name($p1) . ".html#t$time\'>$p1</a> entered as ${seat{$p2}}', |
88 |
} elsif ($type eq 'StaticShape' and $block eq 'StationInventory') { |
'JJ' => '<a href=\'" . safe_name($p1) . ".html#t$time\'>$p1</a> ejected'); |
89 |
push @landmarks, "$z inv $team $x $y"; |
my @teamcol = (0xffffff, 0x0000ff, 0xff00ff); |
90 |
} elsif ($type eq 'StaticShape' and $block eq 'SensorLargePulse') { |
my %seat = ('' => 'a passenger', 'p' => 'pilot', 'w' => 'gunner'); |
91 |
push @landmarks, "$z sensor $team $x $y"; |
my %veh_name = ('BomberFlyer' => 'Bomber', |
92 |
} elsif ($type eq 'WayPoint' and $block eq 'WayPointMarker') { |
'HAPCFlyer' => 'Havoc', |
93 |
push @waypoints, "$team $x $y $z $desc"; |
'MobileBaseVehicle' => 'MPB', |
94 |
} |
'ScoutFlyer' => 'Shrike', |
95 |
} |
'AssaultVehicle' => 'Tank', |
96 |
@landmarks = sort { my ($az, $ra) = split / /, $a, 2; my ($bz, $rb) = split / /, $b, 2; |
'ScoutVehicle' => 'Wildcat'); |
97 |
return $az <=> $bz; } @landmarks; |
|
98 |
|
my ($mission, $mission_type, $mission_name, $mission_type_name, $x0, $y0, $width, $height); |
99 |
$html_index = create_html('Match Summary'); |
my (@landmarks, @waypoints); |
100 |
|
|
101 |
$time = 0; |
print "reading data"; |
102 |
$nicetime = "0:00"; |
read_header(); |
103 |
$imall = create_image(); |
|
104 |
%alive = (); |
my @events = (); |
105 |
@allevents = (); |
my @coords = (); |
106 |
|
my %score = (); |
107 |
while (<>) { |
my %kills = (); |
108 |
chomp; |
my %deaths = (); |
109 |
if ($_ eq '') { |
my %player = (); |
110 |
plot_frame(); |
my %vehicle = (); |
111 |
$time++; |
my %team = (); |
112 |
$secs = $time % 60; |
my $end_time; |
113 |
$nicetime = int($time / 60) . ':' . (($secs < 10) ? "0$secs" : $secs); |
my (@final_score, @team_name); |
114 |
next; |
|
115 |
} |
read_data(); |
116 |
|
print ".\n"; |
117 |
($cmd, $params) = split / /, $_, 2; |
|
118 |
|
my $imall = create_image(); |
119 |
if ($cmd eq 'S' or $cmd eq 'V') { # player spawned / new vehicle |
|
120 |
($id, $team, $name) = split / /, $params, 3; |
my $player; |
121 |
if ($cmd eq 'V') { |
foreach $player (keys %player) { |
122 |
$vname{$id} = $name; |
print "$player"; |
123 |
$name = $id; |
write_player_report($player); |
124 |
|
print ".\n"; |
125 |
|
} |
126 |
|
my $vehicle; |
127 |
|
foreach $vehicle (keys %vehicle) { |
128 |
|
my ($team, $name) = split / /, $vehicle, 2; |
129 |
|
print "$team_name[$team] $veh_name{$name}s"; |
130 |
|
write_vehicle_report($name, $team); |
131 |
|
print ".\n"; |
132 |
|
} |
133 |
|
write_summary_report(); |
134 |
|
|
135 |
|
exit; |
136 |
|
|
137 |
|
#################################################################################################### |
138 |
|
|
139 |
|
sub read_header { |
140 |
|
$mission = <LOG>; chomp $mission; |
141 |
|
$mission_type = <LOG>; chomp $mission_type; |
142 |
|
$mission_name = <LOG>; chomp $mission_name; |
143 |
|
$mission_type_name = <LOG>; chomp $mission_type_name; |
144 |
|
my $area = <LOG>; chomp $area; ($x0, $y0, $width, $height) = split / /, $area; |
145 |
|
|
146 |
|
while (($_ = <LOG>) ne $/) { |
147 |
|
chomp; |
148 |
|
my ($team, $type, $block, $name, $x, $y, $z, $desc) = split / /, $_, 8; |
149 |
|
$x -= $x0; $y = $height - $y + $y0; |
150 |
|
if ($type eq 'StaticShape' and $block eq 'GeneratorLarge') { |
151 |
|
push @landmarks, "$z gen $team $x $y"; |
152 |
|
} elsif ($type eq 'Turret' and $block eq 'TurretBaseLarge') { |
153 |
|
push @landmarks, "$z turret $team $x $y"; |
154 |
|
} elsif ($type eq 'StaticShape' and $block eq 'ExteriorFlagStand') { |
155 |
|
push @landmarks, "$z flag $team $x $y"; |
156 |
|
} elsif ($type eq 'StaticShape' and $block eq 'StationVehicle') { |
157 |
|
push @landmarks, "$z vpad $team $x $y"; |
158 |
|
} elsif ($type eq 'StaticShape' and $block eq 'StationInventory') { |
159 |
|
push @landmarks, "$z inv $team $x $y"; |
160 |
|
} elsif ($type eq 'StaticShape' and $block eq 'SensorLargePulse') { |
161 |
|
push @landmarks, "$z sensor $team $x $y"; |
162 |
|
} elsif ($type eq 'StaticShape' and $block eq 'SolarPanel') { |
163 |
|
push @landmarks, "$z solar $team $x $y"; |
164 |
|
} elsif ($type eq 'WayPoint' and $block eq 'WayPointMarker') { |
165 |
|
push @waypoints, "$team $x $y $z $desc"; |
166 |
} |
} |
167 |
$name{$id} = $name; |
} |
168 |
$life{$name}++; |
|
169 |
$type{$name} = $cmd eq 'S' ? 'player' : 'vehicle'; |
# sort by height for nicer plotting |
170 |
$team{$name} = $team; |
@landmarks = sort { my ($az, $ra) = split / /, $a, 2; my ($bz, $rb) = split / /, $b, 2; |
171 |
$im{$name} = create_image(); |
return $az <=> $bz; } @landmarks; |
172 |
delete $last_pos{$name}; |
} |
173 |
delete $pos{$name}; |
|
174 |
$alive{$name} = 1; |
#################################################################################################### |
175 |
$near{$name} = {}; |
|
176 |
$events{$name} = []; |
sub read_data { |
177 |
if (!exists $html{$name}) { |
my $time = 0; |
178 |
$html{$name} = create_html($name); |
my %name = (); |
179 |
unless ($type{$name} eq 'vehicle') { |
my @flag_carrier; |
180 |
$html{$name} .= <<END; |
my %player_veh; |
181 |
<p>Total score FINALSCORE, deaths FINALDEATHS, kills FINALKILLS.</p> |
|
182 |
END |
while (<LOG>) { |
183 |
} |
chomp; |
184 |
$score{$name} = 0; |
if ($_ eq '') { |
185 |
$deaths{$name} = 0; |
$time++; |
186 |
$kills{$name} = 0; |
next; |
|
} else { |
|
|
$html{$name} .= "</table></div>\n"; |
|
187 |
} |
} |
|
$sname = safe_name($name); |
|
|
$html{$name} .= <<END; |
|
|
<div><h2 id="life$life{$name}">Life $life{$name} ($nicetime - ENDTIME)</h2> |
|
|
<div><a href="${sname}_$life{$name}.png"><img src="s${sname}_$life{$name}.png" |
|
|
alt="" title="Map of life $life{$name}"></a></div> |
|
|
<table> |
|
|
END |
|
|
event($name, "spawned on team $team", $yellow); |
|
188 |
|
|
189 |
} elsif ($cmd eq 'F') { # flag position |
my ($cmd, $params) = split / /, $_, 2; |
190 |
|
|
191 |
} elsif ($cmd eq 'D') { # player damaged |
if ($cmd eq 'S') { # player spawned |
192 |
($victim, $attacker, $weapon) = split / /, $params; |
my ($id, $team, $name) = split / /, $params, 3; |
193 |
|
$name{$id} = $name; |
194 |
|
push @events, [$time, 'S', $name, $team]; |
195 |
|
$player{$name} = 1; |
196 |
|
$team{$name} = $team; |
197 |
|
$player_veh{$name} = 0; |
198 |
|
|
199 |
|
} elsif ($cmd eq 'K') { # player killed |
200 |
|
my ($victim, $killer, $weapon) = split / /, $params; |
201 |
|
$deaths{$name{$victim}}++; |
202 |
|
$kills{$name{$killer}}++ if exists $name{$killer}; |
203 |
|
push @events, [$time, 'K', $name{$victim}, $name{$killer}, $weapon]; |
204 |
|
push @events, [$time, 'KK', $name{$killer}, $name{$victim}, $weapon] |
205 |
|
if exists $name{$killer}; |
206 |
|
if (exists $name{$killer} and $player_veh{$name{$killer}}) { |
207 |
|
push @events, [$time, 'KK', $name{$player_veh{$name{$killer}}}, |
208 |
|
$name{$victim}, $weapon]; |
209 |
|
} |
210 |
|
# if (defined $player_veh{$name}) TODO |
211 |
|
delete $name{$victim}; |
212 |
|
|
213 |
} elsif ($cmd eq 'K' or $cmd eq 'L' or $cmd eq 'W') { # player killed / left / vehicle destroyed |
} elsif ($cmd eq 'L') { # player left |
214 |
if ($cmd eq 'K') { |
my ($id, $score) = split / /, $params; |
215 |
($victim, $killer, $weapon) = split / /, $params; |
push @events, [$time, 'L', $name{$id}]; |
216 |
$victim = $name{$victim}; |
$score{$name{$id}} += $score; |
217 |
$killer = $name{$killer}; |
delete $name{$id}; |
218 |
event($victim, "killed by $DamageTypeText[$weapon]", $red, $killer); |
|
219 |
if (defined $killer) { |
} elsif ($cmd eq 'A') { # armour changed |
220 |
event($killer, "killed ($DamageTypeText[$weapon])", $green, $victim); |
my ($id, $armour) = split / /, $params; |
221 |
$kills{$killer}++; |
push @events, [$time, 'A', $name{$id}, $armour]; |
222 |
|
|
223 |
|
} elsif ($cmd eq 'I') { # inventory changed |
224 |
|
my ($id, $item, $count) = split / /, $params; |
225 |
|
if ($item =~ m/(Pack|SatchelCharge)$/ and $count == 1) { |
226 |
|
$item =~ s/Pack$/ pack/; |
227 |
|
push @events, [$time, 'P', $name{$id}, $item]; |
228 |
} |
} |
|
$deaths{$victim}++; |
|
229 |
|
|
230 |
} elsif ($cmd eq 'L') { |
} elsif ($cmd eq 'D') { # player damaged |
231 |
($victim, $score) = split / /, $params; |
my ($victim, $attacker, $weapon) = split / /, $params; |
|
$victim = $name{$victim}; |
|
|
undef $killer; |
|
|
event($victim, "left the game, score $score", $red); |
|
|
$score{$victim} += $score; |
|
232 |
|
|
233 |
} elsif ($cmd eq 'W') { |
} elsif ($cmd eq 'V') { # new vehicle |
234 |
$victim = $name{$params}; |
my ($id, $team, $name) = split / /, $params, 3; |
235 |
} |
$vehicle{"$team $name"}++; |
236 |
|
$name{$id} = "$team $name " . $vehicle{"$team $name"}; |
237 |
|
$team{$name{$id}} = $team; |
238 |
|
push @events, [$time, 'V', $name{$id}]; |
239 |
|
|
240 |
|
} elsif ($cmd eq 'W') { # vehicle destroyed |
241 |
|
my $id = $params; |
242 |
|
push @events, [$time, 'W', $name{$id}]; |
243 |
|
#delete $name{$id}; |
244 |
|
|
245 |
|
} elsif ($cmd eq 'Z') { # final score |
246 |
|
my ($team, $score, $team_name) = split / /, $params, 3; |
247 |
|
$final_score[$team] = $score; |
248 |
|
$team_name[$team] = $team_name; |
249 |
|
|
250 |
|
} elsif ($cmd eq 'F') { # flag position |
251 |
|
my ($team, @data) = split / /, $params; |
252 |
|
my $carrier; |
253 |
|
$flag_carrier[$team] = 0 unless defined $flag_carrier[$team]; |
254 |
|
if (@data == 3) { |
255 |
|
my ($x, $y, $z) = @data; |
256 |
|
$x -= $x0; $y = $height - $y + $y0; |
257 |
|
$carrier = 0; |
258 |
|
} else { |
259 |
|
$carrier = $name{$data[0]}; |
260 |
|
} |
261 |
|
if ($carrier ne $flag_carrier[$team]) { |
262 |
|
if ($flag_carrier[$team]) { |
263 |
|
push @events, [$time, 'F-', $flag_carrier[$team], $team]; |
264 |
|
} |
265 |
|
if ($carrier) { |
266 |
|
push @events, [$time, 'F+', $carrier, $team]; |
267 |
|
} |
268 |
|
$flag_carrier[$team] = $carrier; |
269 |
|
} |
270 |
|
|
271 |
foreach $name (keys %near) { |
} else { # player / vehicle position |
272 |
delete ${$near{$name}}{$victim} if exists ${$near{$name}}{$victim}; |
my $id = $cmd; |
273 |
|
next unless exists $name{$id}; |
274 |
|
my $name = $name{$id}; |
275 |
|
my ($x, $y, $z, $vehicle, $seat) = split / /, $params; |
276 |
|
$x -= $x0; $y = $height - $y + $y0; |
277 |
|
${$coords[$time]}{$name} = "$x $y $z"; |
278 |
|
next unless defined $player{$name}; |
279 |
|
if ($player_veh{$name} != $vehicle) { |
280 |
|
if ($player_veh{$name}) { |
281 |
|
push @events, [$time, 'J', $name]; |
282 |
|
push @events, [$time, 'JJ', |
283 |
|
$name{$player_veh{$name}}, $name]; |
284 |
|
} |
285 |
|
if ($vehicle != 0) { |
286 |
|
push @events, [$time, 'G', $name, |
287 |
|
$name{$vehicle}, $seat]; |
288 |
|
push @events, [$time, 'GG', $name{$vehicle}, |
289 |
|
$name, $seat]; |
290 |
|
} |
291 |
|
$player_veh{$name} = $vehicle; |
292 |
|
} |
293 |
} |
} |
294 |
|
} |
295 |
|
$end_time = $time + 1; |
296 |
|
} |
297 |
|
|
298 |
|
#################################################################################################### |
299 |
|
|
300 |
if ($im{$victim}) { |
sub write_player_report { |
301 |
plot_events($victim); |
my $player = shift; |
302 |
$sname = safe_name($victim); |
|
303 |
save_image($im{$victim}, "${sname}_$life{$victim}.png"); |
my $score = exists $score{$player} ? $score{$player} : 0; |
304 |
|
my $deaths = exists $deaths{$player} ? $deaths{$player} : 0; |
305 |
|
my $kills = exists $kills{$player} ? $kills{$player} : 0; |
306 |
|
my $sname = safe_name($player); |
307 |
|
my $html = create_html($player); |
308 |
|
|
309 |
|
$html .= <<END; |
310 |
|
<p>Total score $score, deaths $deaths, kills $kills. <a href="./">Match summary.</a></p> |
311 |
|
END |
312 |
|
|
313 |
|
# find spawn / death times |
314 |
|
my @spawn = map $$_[0], (grep {$$_[2] eq $player and $$_[1] eq 'S'} @events); |
315 |
|
my @death = map $$_[0], (grep {$$_[2] eq $player and |
316 |
|
($$_[1] eq 'K' or $$_[1] eq 'L')} @events); |
317 |
|
push @spawn, $end_time; |
318 |
|
|
319 |
|
for (my $life = 0; $life != @spawn - 1; $life++) { |
320 |
|
my $nicetime = nice_time($spawn[$life]); |
321 |
|
my $nicedtime = nice_time($death[$life]); |
322 |
|
my $life1 = $life + 1; |
323 |
|
$html .= <<END; |
324 |
|
<div><h2 id="life$life1">Life $life1 ($nicetime - $nicedtime)</h2> |
325 |
|
<div><a href="${sname}_$life1.png"><img src="s${sname}_$life1.png" |
326 |
|
alt="" title="Map of life $life1"></a></div> |
327 |
|
END |
328 |
|
$html .= player_life($player, "${sname}_$life1.png", |
329 |
|
$spawn[$life], $spawn[$life1] - 1); |
330 |
|
$html .= '</div>'; |
331 |
|
} |
332 |
|
|
333 |
|
save_html(safe_name($player) . '.html', $html); |
334 |
|
} |
335 |
|
|
336 |
|
#################################################################################################### |
337 |
|
|
338 |
|
sub player_life { |
339 |
|
my $player = shift; |
340 |
|
my $imname = shift; |
341 |
|
my $time0 = shift; |
342 |
|
my $time1 = shift; |
343 |
|
|
344 |
|
my $prev_time = -1; |
345 |
|
|
346 |
|
my $html = "<table>\n"; |
347 |
|
|
348 |
|
my $im = create_image(); |
349 |
|
plot_route($im, $player, $time0, $time1); |
350 |
|
|
351 |
|
my @evs = grep {$$_[2] eq $player and $time0 <= $$_[0] and $$_[0] <= $time1} @events; |
352 |
|
|
353 |
|
my $event; |
354 |
|
foreach $event (@evs) { |
355 |
|
my ($time, $ev, $me, $p1, $p2) = @$event; |
356 |
|
|
357 |
|
my $nicetime = nice_time($time); |
358 |
|
if ($time == $prev_time) { |
359 |
|
$html .= '<tr><td></td>'; |
360 |
|
} else { |
361 |
|
$html .= "<tr id='t$time'><td class='time'>$nicetime</td>"; |
362 |
} |
} |
|
delete $alive{$victim}; |
|
|
$html{$victim} =~ s/ENDTIME/$nicetime/; |
|
363 |
|
|
364 |
} elsif ($cmd eq 'A') { # armour changed |
my $enemy = ''; |
365 |
($id, $armour) = split / /, $params; |
if (($ev eq 'K' or $ev eq 'KK') and defined $p1) { |
366 |
$name = $name{$id}; |
my $sname_en = safe_name($p1); |
367 |
$armour{$name} = $armour; |
$enemy = "<a href='$sname_en.html#t$time'>$p1</a>"; |
|
event($name, "$armour armour", $orange); |
|
|
|
|
|
} elsif ($cmd eq 'Z') { # final score |
|
|
|
|
|
} else { # player / vehicle position |
|
|
$id = $cmd; |
|
|
$name = $name{$id}; |
|
|
next unless (exists $name{$id} and exists $im{$name}); |
|
|
($x, $y, $z, $vehicle, $seat) = split / /, $params; |
|
|
$x -= $x0; $y = $height - $y + $y0; |
|
|
$pos{$name} = "$x $y $z"; |
|
|
if (exists $pending{$name}) { |
|
|
push @{$events{$name}}, "$x $y $z $pending{$name}"; |
|
|
delete $pending{$name}; |
|
368 |
} |
} |
369 |
|
my $desc = eval '"' . $desc{$ev} . '"'; |
370 |
|
my $hexcol = sprintf "%.6lx", $colour{$ev}; |
371 |
|
$html .= "<td style='color: #$hexcol'>$desc</td></tr>\n"; |
372 |
|
|
373 |
|
if (exists ${$coords[$time]}{$player}) { |
374 |
|
my ($x, $y, $z) = split / /, ${$coords[$time]}{$player}; |
375 |
|
square($im, $x, $y, 8, $colour{$ev}); |
376 |
|
} elsif (exists ${$coords[$time - 1]}{$player}) { |
377 |
|
my ($x, $y, $z) = split / /, ${$coords[$time - 1]}{$player}; |
378 |
|
square($im, $x, $y, 8, $colour{$ev}); |
379 |
|
} |
380 |
|
$prev_time = $time; |
381 |
|
} |
382 |
|
|
383 |
|
$html .= "</table>\n"; |
384 |
|
save_image($im, $imname); |
385 |
|
|
386 |
|
return $html; |
387 |
|
} |
388 |
|
|
389 |
|
#################################################################################################### |
390 |
|
|
391 |
|
sub plot_route { |
392 |
|
my $image = shift; |
393 |
|
my $name = shift; |
394 |
|
my $t0 = shift; |
395 |
|
my $t1 = shift; |
396 |
|
my %near = (); |
397 |
|
for (my $t = $t0; $t != $t1; $t++) { |
398 |
|
my $s = ($t % 10) == 0 ? 2 : 1; |
399 |
|
if (exists ${$coords[$t]}{$name}) { |
400 |
|
my ($x0, $y0, $z0) = split / /, ${$coords[$t]}{$name}; |
401 |
|
my $near; |
402 |
|
foreach $near (keys %near) { |
403 |
|
if (exists ${$coords[$t]}{$near}) { |
404 |
|
my ($xn, $yn, $zn) = split / /, ${$coords[$t]}{$near}; |
405 |
|
delete $near{$near} if $near1 < distance($x0, $y0, $z0, |
406 |
|
$xn, $yn, $zn); |
407 |
|
} else { |
408 |
|
delete $near{$near}; |
409 |
|
} |
410 |
|
} |
411 |
|
foreach $near (keys %player) { |
412 |
|
next if $near eq $name; |
413 |
|
next if exists $near{$near}; |
414 |
|
if (exists ${$coords[$t]}{$near}) { |
415 |
|
my ($xn, $yn, $zn) = split / /, ${$coords[$t]}{$near}; |
416 |
|
if (distance($x0, $y0, $z0, $xn, $yn, $zn) < $near0) { |
417 |
|
$near{$near} = 1; |
418 |
|
line($image, $x0, $y0, $xn, $yn, 0x444444); |
419 |
|
text($image, $xn, $yn, $near); |
420 |
|
} |
421 |
|
} |
422 |
|
} |
423 |
|
foreach $near (keys %near) { |
424 |
|
my ($xn0, $yn0, $zn0) = split / /, ${$coords[$t]}{$near}; |
425 |
|
my $colour = $team{$name} == $team{$near} ? 0x00ff00 : 0xff0000; |
426 |
|
line($image, $x0, $y0, $xn0, $yn0, 0x444444) if $s == 2; |
427 |
|
if (exists ${$coords[$t + 1]}{$near}) { |
428 |
|
my ($xn1, $yn1, $zn1) = split / /, |
429 |
|
${$coords[$t + 1]}{$near}; |
430 |
|
line($image, $xn0, $yn0, $xn1, $yn1, $colour); |
431 |
|
} |
432 |
|
square($image, $xn0, $yn0, $s, $colour); |
433 |
|
} |
434 |
|
if (exists ${$coords[$t + 1]}{$name}) { |
435 |
|
my ($x1, $y1, $z1) = split / /, ${$coords[$t + 1]}{$name}; |
436 |
|
line($image, $x0, $y0, $x1, $y1, $colour_me); |
437 |
|
line($imall, $x0, $y0, $x1, $y1, $teamcol[$team{$name}]); |
438 |
|
} |
439 |
|
square($image, $x0, $y0, $s, $colour_me); |
440 |
|
text($image, $x0, $y0, nice_time($t)) if $s == 2; |
441 |
|
} |
442 |
} |
} |
443 |
} |
} |
444 |
|
|
445 |
foreach $victim (keys %alive) { |
#################################################################################################### |
446 |
if ($im{$victim}) { |
|
447 |
plot_events($victim); |
sub write_vehicle_report { |
448 |
$sname = safe_name($victim); |
my $name = shift; |
449 |
save_image($im{$victim}, "${sname}_$life{$victim}.png"); |
my $team = shift; |
450 |
|
|
451 |
|
my $sname = safe_name($name); |
452 |
|
my $html = create_html($team_name[$team] . ' ' . $veh_name{$name} . 's'); |
453 |
|
$html .= '<p><a href="./">Match summary.</a></p>'; |
454 |
|
|
455 |
|
my $run; |
456 |
|
for ($run = 1; $run != $vehicle{"$team $name"} + 1; $run++) { |
457 |
|
my @ev = grep {$$_[2] eq "$team $name $run"} @events; |
458 |
|
my $time0 = ${$ev[0]}[0]; |
459 |
|
my $time1 = ${$ev[-1]}[1] eq 'W' ? ${$ev[-1]}[0] : $end_time; |
460 |
|
my $nicetime = nice_time($time0); |
461 |
|
my $nicedtime = nice_time($time1); |
462 |
|
|
463 |
|
$html .= <<END; |
464 |
|
<div><h2 id="life$run">$veh_name{$name} $run ($nicetime - $nicedtime)</h2> |
465 |
|
<div><a href="${team}_${sname}_$run.png"><img src="s${team}_${sname}_$run.png" |
466 |
|
alt="" title="Map of $veh_name{$name} $run"></a></div> |
467 |
|
END |
468 |
|
$html .= player_life("$team $name $run", "${team}_${sname}_$run.png", |
469 |
|
$time0, $time1); |
470 |
|
$html .= '</div>'; |
471 |
} |
} |
472 |
|
|
473 |
|
save_html("${team}_$sname.html", $html); |
474 |
|
} |
475 |
|
|
476 |
|
#################################################################################################### |
477 |
|
|
478 |
|
sub vehicle_link { |
479 |
|
my $s = shift; |
480 |
|
my ($team, $name, $run) = split / /, $s; |
481 |
|
return "<a href='${team}_$name.html#life$run'>$team_name[$team] $veh_name{$name} $run</a>"; |
482 |
} |
} |
483 |
|
|
484 |
$html_index .= <<END; |
#################################################################################################### |
485 |
|
|
486 |
|
sub write_summary_report { |
487 |
|
my $html = create_html("Match Summary"); |
488 |
|
my $name; |
489 |
|
my $nicetime = nice_time($end_time); |
490 |
|
my $i; |
491 |
|
|
492 |
|
$html .= <<END; |
493 |
<div><a href="Overview.png"><img src="sOverview.png" |
<div><a href="Overview.png"><img src="sOverview.png" |
494 |
alt="" title="Overview map"></a></div> |
alt="" title="Overview map"></a></div> |
495 |
<p>Playing time: $nicetime</p> |
<p>Playing time $nicetime</p> |
496 |
|
<table><tr><th>Final scores</th></tr> |
497 |
|
END |
498 |
|
for ($i = 1; $i != @team_name; $i++) { |
499 |
|
$html .= "<tr><td>${team_name[$i]}</td><td>${final_score[$i]}</td></tr>\n"; |
500 |
|
} |
501 |
|
|
502 |
|
$html .= <<END; |
503 |
|
</table> |
504 |
<table> |
<table> |
505 |
<tr><th>Player</th><th>Team</th><th>Score</th><th>Deaths</th><th>Kills</th></tr> |
<tr><th>Player</th><th>Team</th><th>Score</th><th>Deaths</th><th>Kills</th></tr> |
506 |
END |
END |
507 |
while (($name, $html) = each %html) { |
|
508 |
$html .= "</table></div>\n"; |
foreach $name (sort keys %player) { |
509 |
$html =~ s/FINALSCORE/$score{$name}/; |
my $sname = safe_name($name); |
510 |
$html =~ s/FINALDEATHS/$deaths{$name}/; |
my $score = exists $score{$name} ? $score{$name} : 0; |
511 |
$html =~ s/FINALKILLS/$kills{$name}/; |
my $deaths = exists $deaths{$name} ? $deaths{$name} : 0; |
512 |
$sname = safe_name($name); |
my $kills = exists $kills{$name} ? $kills{$name} : 0; |
513 |
save_html("$sname.html", $html); |
$html .= <<END; |
514 |
if ($type{$name} eq 'player') { |
<tr><td><a href="$sname.html">$name</a></td><td>$team_name[$team{$name}]</td><td>$score</td><td>$deaths</td><td>$kills</td></tr> |
|
$html_index .= <<END; |
|
|
<tr><td><a href="$sname.html">$name</a></td><td>$team{$name}</td><td>$score{$name}</td><td>$deaths{$name}</td><td>$kills{$name}</td></tr> |
|
515 |
END |
END |
516 |
} |
} |
|
} |
|
|
|
|
|
$html_index .= "</table>\n"; |
|
|
|
|
517 |
|
|
518 |
#$html_index .= "<h2>Timeline<h2>\n<table class='timeline'>\n"; |
$html .= "</table>\n"; |
|
$header = "<tr><td></td>"; |
|
|
foreach $n (keys %type) { |
|
|
$header .= "<th>$n</th>"; |
|
|
} |
|
|
$header .= "</tr>\n"; |
|
|
#$html_index .= $header; |
|
|
$time = 0; |
|
|
%event = (); |
|
|
foreach $e (@allevents) { |
|
|
($t, $colour, $name) = split / /, $e, 3; |
|
|
while ($time < $t) { |
|
|
$secs = $time % 60; |
|
|
$nicetime = int($time / 60) . ':' . (($secs < 10) ? "0$secs" : $secs); |
|
|
# $html_index .= "<tr><td>$nicetime</td>"; |
|
|
foreach $n (keys %type) { |
|
|
if (exists $event{$n}) { |
|
|
$hexcol = sprintf "%.6lx", $event{$n}; |
|
|
# $html_index .= "<td style='background-color: #$hexcol'> </td>"; |
|
|
} else { |
|
|
# $html_index .= "<td> </td>"; |
|
|
} |
|
|
} |
|
|
%event = (); |
|
|
# $html_index .= "</tr>\n"; |
|
|
$time++; |
|
|
# $html_index .= $header if (($time % 30) == 0); |
|
|
} |
|
|
$event{$name} = $colour; |
|
|
} |
|
|
|
|
|
save_image($imall, "Overview.png"); |
|
|
#$html_index .= "</table>\n"; |
|
|
save_html("index.html", $html_index); |
|
|
|
|
|
|
|
|
sub plot_frame { |
|
|
$s = 1; |
|
|
$s = 2 if ($time % 10) == 0; |
|
|
foreach $name (keys %alive) { |
|
|
next unless exists $pos{$name}; |
|
|
($x, $y, $z) = split / /, $pos{$name}; |
|
|
%near0 = %{$near{$name}}; |
|
|
foreach $name1 (keys %alive) { |
|
|
next if $name1 eq $name; |
|
|
($x1, $y1, $z1) = split / /, $pos{$name1}; |
|
|
my $dist = distance($x, $y, $z, $x1, $y1, $z1); |
|
|
if (exists ${$near{$name}}{$name1}) { |
|
|
delete ${$near{$name}}{$name1} if $near1 < $dist; |
|
|
} elsif ($dist < $near0) { |
|
|
($x1, $y1, $z1) = split / /, $pos{$name1}; |
|
|
text($im{$name}, $x1 + 4, $y1, $name1); |
|
|
${$near{$name}}{$name1} = 1; |
|
|
} |
|
|
} |
|
|
foreach $name1 (keys %{$near{$name}}) { |
|
|
($x1, $y1, $z1) = split / /, $pos{$name1}; |
|
|
my $colour = $team{$name1} == $team{$name} ? $green : $red; |
|
|
if ($last_pos{$name1}) { |
|
|
($xp, $yp, $zp) = split / /, $last_pos{$name1}; |
|
|
line($im{$name}, $xp, $yp, $x1, $y1, $colour); |
|
|
} |
|
|
square($im{$name}, $x1, $y1, $s, $colour); |
|
|
line($im{$name}, $x, $y, $x1, $y1, $grey) if $s == 2; |
|
|
} |
|
|
text($im{$name}, $x, $y, $nicetime) if $s == 2; |
|
|
if (exists $last_pos{$name}) { |
|
|
($xp, $yp, $zp) = split / /, $last_pos{$name}; |
|
|
line($im{$name}, $xp, $yp, $x, $y, $white); |
|
|
line($imall, $xp, $yp, $x, $y, $teamcol[$team{$name}]); |
|
|
} |
|
|
square($im{$name}, $x, $y, $s, $white); |
|
519 |
|
|
520 |
|
$html .= "<table><tr><th>Vehicles</th><th>Total used</th></tr>\n"; |
521 |
|
foreach $vehicle (sort keys %vehicle) { |
522 |
|
my ($team, $name) = split / /, $vehicle, 2; |
523 |
|
my $sname = safe_name($name); |
524 |
|
$html .= <<END; |
525 |
|
<tr><td><a href="${team}_$sname.html">$team_name[$team] $veh_name{$name}s</a></td><td>${vehicle{"$team $name"}}</td></tr> |
526 |
|
END |
527 |
} |
} |
528 |
%last_pos = %pos; |
$html .= "</table>\n"; |
529 |
%pos = (); |
|
530 |
|
save_html('index.html', $html); |
531 |
|
save_image($imall, 'Overview.png'); |
532 |
} |
} |
533 |
|
|
534 |
|
#################################################################################################### |
535 |
|
|
536 |
sub create_image { |
sub create_image { |
537 |
my $image = GD::Image->newTrueColor($width, $height); |
my $image = GD::Image->newTrueColor($width, $height); |
538 |
my $lm; |
my ($lm, $g); |
539 |
foreach $lm (@landmarks) { |
foreach $lm (@landmarks) { |
540 |
my ($z, $type, $team, $x, $y) = split / /, $lm; |
my ($z, $type, $team, $x, $y) = split / /, $lm; |
541 |
icon($image, $icon{$type}, $x, $y); |
icon($image, $icon{$type}, $x, $y); |
551 |
my $image = shift; |
my $image = shift; |
552 |
my $file = shift; |
my $file = shift; |
553 |
my $png = $image->png; |
my $png = $image->png; |
554 |
open PNG, ">$file" or die "failed to open $file: $!"; |
open PNG, ">$outdir/$file" or die "failed to open $outdir/$file: $!"; |
555 |
binmode PNG; |
binmode PNG; |
556 |
print PNG $png; |
print PNG $png; |
557 |
close PNG; |
close PNG; |
562 |
$thumb->copyResampled($image, 0, 0, 0, 0, $thumbsize * $width / $max, |
$thumb->copyResampled($image, 0, 0, 0, 0, $thumbsize * $width / $max, |
563 |
$thumbsize * $height / $max, $width, $height); |
$thumbsize * $height / $max, $width, $height); |
564 |
$png = $thumb->png; |
$png = $thumb->png; |
565 |
open PNG, ">s$file" or die "failed to open s$file: $!"; |
open PNG, ">$outdir/s$file" or die "failed to open $outdir/s$file: $!"; |
566 |
binmode PNG; |
binmode PNG; |
567 |
print PNG $png; |
print PNG $png; |
568 |
close PNG; |
close PNG; |
589 |
$image->line($x0, $y0, $x1, $y1, $colour); |
$image->line($x0, $y0, $x1, $y1, $colour); |
590 |
} |
} |
591 |
|
|
|
sub distance { |
|
|
return sqrt (($_[0] - $_[3]) ** 2 + ($_[1] - $_[4]) ** 2 + ($_[2] - $_[5]) ** 2); |
|
|
} |
|
|
|
|
592 |
sub load_image { |
sub load_image { |
593 |
my $file = shift; |
my $file = shift; |
594 |
open PNG, $file or die "failed to open $file: $!"; |
open PNG, $file or die "failed to open $file: $!"; |
612 |
my $x = shift; |
my $x = shift; |
613 |
my $y = shift; |
my $y = shift; |
614 |
my $text = shift; |
my $text = shift; |
615 |
$image->string(gdSmallFont, $x, $y, $text, $white); |
$image->string(gdSmallFont, $x, $y, $text, 0xffffff); |
616 |
} |
} |
617 |
|
|
618 |
|
#################################################################################################### |
619 |
|
|
620 |
sub create_html { |
sub create_html { |
621 |
my $title = shift; |
my $title = shift; |
622 |
my $html = <<END; |
my $html = <<END; |
639 |
my $file = shift; |
my $file = shift; |
640 |
my $html = shift; |
my $html = shift; |
641 |
$html .= "</body></html>\n"; |
$html .= "</body></html>\n"; |
642 |
open HTML, ">$file" or die "failed to open $file: $!"; |
open HTML, ">$outdir/$file" or die "failed to open $outdir/$file: $!"; |
643 |
print HTML $html; |
print HTML $html; |
644 |
close HTML; |
close HTML; |
645 |
} |
} |
646 |
|
|
647 |
|
#################################################################################################### |
648 |
|
|
649 |
sub safe_name { |
sub safe_name { |
650 |
my $s = shift; |
my $s = shift; |
651 |
$s =~ tr/[a-zA-Z0-9]/_/cs; |
$s =~ tr/[a-zA-Z0-9]/_/cs; |
652 |
return $s; |
return $s; |
653 |
} |
} |
654 |
|
|
655 |
sub event { |
sub nice_time { |
656 |
my $name = shift; |
my $time = shift; |
657 |
return unless exists $html{$name}; |
my $secs = $time % 60; |
658 |
my $desc = shift; |
return int($time / 60) . ':' . (($secs < 10) ? "0$secs" : $secs); |
|
my $colour = shift; |
|
|
my $name2 = shift; |
|
|
print "$nicetime\t$name $desc"; |
|
|
my $hexcol = sprintf "%.6lx", $colour; |
|
|
my $line = "<tr id='t$time'><td class='time'>$nicetime</td><td style='color: #$hexcol'>$desc"; |
|
|
if (defined $name2 and exists $html{$name2}) { |
|
|
print " $name2"; |
|
|
$sname = safe_name($name2); |
|
|
$line .= " <a href='$sname.html#t$time'>$name2</a>"; |
|
|
} |
|
|
print "\n"; |
|
|
$line .= "</td></tr>\n"; |
|
|
$html{$name} .= $line; |
|
|
if (exists $last_pos{$name}) { |
|
|
push @{$events{$name}}, "$last_pos{$name} $colour $desc"; |
|
|
} else { |
|
|
$pending{$name} = "$colour $desc"; |
|
|
} |
|
|
push @allevents, "$time $colour $name"; |
|
659 |
} |
} |
660 |
|
|
661 |
sub plot_events { |
sub distance { |
662 |
my $name = shift; |
return sqrt (($_[0] - $_[3]) ** 2 + ($_[1] - $_[4]) ** 2 + ($_[2] - $_[5]) ** 2); |
|
foreach $e (@{$events{$name}}) { |
|
|
my ($x, $y, $z, $colour, $desc) = split / /, $e, 5; |
|
|
square($im{$name}, $x, $y, 8, $colour); |
|
|
} |
|
663 |
} |
} |
664 |
|
|
665 |
|
#################################################################################################### |
666 |
|
|