/[james]/t2matchlog/t2matchlog
ViewVC logotype

Contents of /t2matchlog/t2matchlog

Parent Directory Parent Directory | Revision Log Revision Log


Revision 37 - (show annotations) (download)
Sat Nov 29 23:01:29 2003 UTC (20 years, 5 months ago) by james
File size: 19966 byte(s)
Rewrite with many improvements.

1 #!/bin/perl -W
2
3 use strict;
4 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";
13 $| = 1;
14
15 my $near0 = 70;
16 my $near1 = 200;
17 my $thumbsize = 400;
18
19 # from damageTypes.cs
20 my @DamageTypeText;
21 $DamageTypeText[0] = 'default';
22 $DamageTypeText[1] = 'blaster';
23 $DamageTypeText[2] = 'plasma';
24 $DamageTypeText[3] = 'chaingun';
25 $DamageTypeText[4] = 'disc';
26 $DamageTypeText[5] = 'grenade';
27 $DamageTypeText[6] = 'laser';
28 $DamageTypeText[7] = 'ELF';
29 $DamageTypeText[8] = 'mortar';
30 $DamageTypeText[9] = 'missile';
31 $DamageTypeText[10] = 'shocklance';
32 $DamageTypeText[11] = 'mine';
33 $DamageTypeText[12] = 'explosion';
34 $DamageTypeText[13] = 'impact';
35 $DamageTypeText[14] = 'ground';
36 $DamageTypeText[15] = 'turret';
37 $DamageTypeText[16] = 'plasma turret';
38 $DamageTypeText[17] = 'AA turret';
39 $DamageTypeText[18] = 'ELF turret';
40 $DamageTypeText[19] = 'mortar turret';
41 $DamageTypeText[20] = 'missile turret';
42 $DamageTypeText[21] = 'clamp turret';
43 $DamageTypeText[22] = 'spike turret';
44 $DamageTypeText[23] = 'sentry turret';
45 $DamageTypeText[24] = 'out of bounds';
46 $DamageTypeText[25] = 'lava';
47 $DamageTypeText[26] = 'shrike blaster';
48 $DamageTypeText[27] = 'belly turret';
49 $DamageTypeText[28] = 'bomber bomb';
50 $DamageTypeText[29] = 'tank chaingun';
51 $DamageTypeText[30] = 'tank mortar';
52 $DamageTypeText[31] = 'satchel charge';
53 $DamageTypeText[32] = 'MPB missile';
54 $DamageTypeText[33] = 'lighting';
55 $DamageTypeText[35] = 'ForceField';
56 $DamageTypeText[36] = 'Crash';
57 $DamageTypeText[98] = 'nexus camping';
58 $DamageTypeText[99] = 'suicide';
59
60 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");
63 $icon{'gen'} = load_image("icons/com_icon_generator.png");
64 $icon{'turret'} = load_image("icons/com_icon_turretbase.png");
65 $icon{'inv'} = load_image("icons/com_icon_inventory.png");
66 $icon{'sensor'} = load_image("icons/com_icon_sensor.png");
67 $icon{'vpad'} = load_image("icons/com_icon_vehicle_inventory.png");
68 $icon{'solar'} = load_image("icons/com_icon_solar_gen.png");
69
70 my %colour = ('S' => 0xffff00, 'A' => 0xff8000, 'KK' => 0x00ff00, 'K' => 0xff0000,
71 'L' => 0xff8000, 'F+' => 0x00ffff, 'F-' => 0xff00ff, 'P' => 0xff8000,
72 'V' => 0xffff00, 'W' => 0xff0000, 'G' => 0x0080ff, 'J' => 0x00ff80,
73 'GG' => 0x0080ff, 'JJ' => 0x00ff80);
74 my $colour_me = 0xffffff;
75 my %desc = ('S' => 'spawned on team $team_name[$p1]',
76 'A' => '$p1 armour',
77 'KK' => 'killed $enemy using $DamageTypeText[$p2]',
78 'K' => 'killed by $DamageTypeText[$p2] $enemy',
79 'L' => 'left the game',
80 'F+' => 'took the $team_name[$p1] flag',
81 'F-' => 'dropped the $team_name[$p1] flag',
82 'P' => '$p1',
83 'V' => 'vehicle created',
84 'W' => 'vehicle destroyed',
85 'G' => 'entered " . vehicle_link($p1) . " as ${seat{$p2}}',
86 'J' => 'ejected from vehicle',
87 'GG' => '<a href=\'" . safe_name($p1) . ".html#t$time\'>$p1</a> entered as ${seat{$p2}}',
88 'JJ' => '<a href=\'" . safe_name($p1) . ".html#t$time\'>$p1</a> ejected');
89 my @teamcol = (0xffffff, 0x0000ff, 0xff00ff);
90 my %seat = ('' => 'a passenger', 'p' => 'pilot', 'w' => 'gunner');
91 my %veh_name = ('BomberFlyer' => 'Bomber',
92 'HAPCFlyer' => 'Havoc',
93 'MobileBaseVehicle' => 'MPB',
94 'ScoutFlyer' => 'Shrike',
95 'AssaultVehicle' => 'Tank',
96 'ScoutVehicle' => 'Wildcat');
97
98 my ($mission, $mission_type, $mission_name, $mission_type_name, $x0, $y0, $width, $height);
99 my (@landmarks, @waypoints);
100
101 print "reading data";
102 read_header();
103
104 my @events = ();
105 my @coords = ();
106 my %score = ();
107 my %kills = ();
108 my %deaths = ();
109 my %player = ();
110 my %vehicle = ();
111 my %team = ();
112 my $end_time;
113 my (@final_score, @team_name);
114
115 read_data();
116 print ".\n";
117
118 my $imall = create_image();
119
120 my $player;
121 foreach $player (keys %player) {
122 print "$player";
123 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 }
168
169 # sort by height for nicer plotting
170 @landmarks = sort { my ($az, $ra) = split / /, $a, 2; my ($bz, $rb) = split / /, $b, 2;
171 return $az <=> $bz; } @landmarks;
172 }
173
174 ####################################################################################################
175
176 sub read_data {
177 my $time = 0;
178 my %name = ();
179 my @flag_carrier;
180 my %player_veh;
181
182 while (<LOG>) {
183 chomp;
184 if ($_ eq '') {
185 $time++;
186 next;
187 }
188
189 my ($cmd, $params) = split / /, $_, 2;
190
191 if ($cmd eq 'S') { # player spawned
192 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 'L') { # player left
214 my ($id, $score) = split / /, $params;
215 push @events, [$time, 'L', $name{$id}];
216 $score{$name{$id}} += $score;
217 delete $name{$id};
218
219 } elsif ($cmd eq 'A') { # armour changed
220 my ($id, $armour) = split / /, $params;
221 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 }
229
230 } elsif ($cmd eq 'D') { # player damaged
231 my ($victim, $attacker, $weapon) = split / /, $params;
232
233 } elsif ($cmd eq 'V') { # new vehicle
234 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 } else { # player / vehicle position
272 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 sub write_player_report {
301 my $player = shift;
302
303 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 }
363
364 my $enemy = '';
365 if (($ev eq 'K' or $ev eq 'KK') and defined $p1) {
366 my $sname_en = safe_name($p1);
367 $enemy = "<a href='$sname_en.html#t$time'>$p1</a>";
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 ####################################################################################################
446
447 sub write_vehicle_report {
448 my $name = shift;
449 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 ####################################################################################################
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"
494 alt="" title="Overview map"></a></div>
495 <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>
505 <tr><th>Player</th><th>Team</th><th>Score</th><th>Deaths</th><th>Kills</th></tr>
506 END
507
508 foreach $name (sort keys %player) {
509 my $sname = safe_name($name);
510 my $score = exists $score{$name} ? $score{$name} : 0;
511 my $deaths = exists $deaths{$name} ? $deaths{$name} : 0;
512 my $kills = exists $kills{$name} ? $kills{$name} : 0;
513 $html .= <<END;
514 <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>
515 END
516 }
517
518 $html .= "</table>\n";
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 $html .= "</table>\n";
529
530 save_html('index.html', $html);
531 save_image($imall, 'Overview.png');
532 }
533
534 ####################################################################################################
535
536 sub create_image {
537 my $image = GD::Image->newTrueColor($width, $height);
538 my ($lm, $g);
539 foreach $lm (@landmarks) {
540 my ($z, $type, $team, $x, $y) = split / /, $lm;
541 icon($image, $icon{$type}, $x, $y);
542 }
543 foreach $g (@waypoints) {
544 my ($team, $x, $y, $z, $desc) = split / /, $g, 5;
545 text($image, $x, $y, $desc);
546 }
547 return $image;
548 }
549
550 sub save_image {
551 my $image = shift;
552 my $file = shift;
553 my $png = $image->png;
554 open PNG, ">$outdir/$file" or die "failed to open $outdir/$file: $!";
555 binmode PNG;
556 print PNG $png;
557 close PNG;
558
559 my $max = $width < $height ? $height : $width;
560 my $thumb = GD::Image->newTrueColor($thumbsize * $width / $max,
561 $thumbsize * $height / $max);
562 $thumb->copyResampled($image, 0, 0, 0, 0, $thumbsize * $width / $max,
563 $thumbsize * $height / $max, $width, $height);
564 $png = $thumb->png;
565 open PNG, ">$outdir/s$file" or die "failed to open $outdir/s$file: $!";
566 binmode PNG;
567 print PNG $png;
568 close PNG;
569 }
570
571 sub square {
572 my $image = shift;
573 return unless defined $image;
574 my $x = shift;
575 my $y = shift;
576 my $s = shift;
577 my $colour = shift;
578 $image->filledRectangle($x - $s, $y - $s, $x + $s, $y + $s, $colour);
579 }
580
581 sub line {
582 my $image = shift;
583 return unless defined $image;
584 my $x0 = shift;
585 my $y0 = shift;
586 my $x1 = shift;
587 my $y1 = shift;
588 my $colour = shift;
589 $image->line($x0, $y0, $x1, $y1, $colour);
590 }
591
592 sub load_image {
593 my $file = shift;
594 open PNG, $file or die "failed to open $file: $!";
595 my $im = GD::Image->newFromPng(\*PNG) or die "PNG load of $file failed";
596 close PNG;
597 $im->transparent(0);
598 return $im;
599 }
600
601 sub icon {
602 my $image = shift;
603 my $icon = shift;
604 my $x = shift;
605 my $y = shift;
606 $image->setBrush($icon);
607 $image->setPixel($x, $y, gdBrushed);
608 }
609
610 sub text {
611 my $image = shift;
612 my $x = shift;
613 my $y = shift;
614 my $text = shift;
615 $image->string(gdSmallFont, $x, $y, $text, 0xffffff);
616 }
617
618 ####################################################################################################
619
620 sub create_html {
621 my $title = shift;
622 my $html = <<END;
623 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
624 "http://www.w3.org/TR/html4/strict.dtd">
625 <html>
626 <head>
627 <link rel="stylesheet" type="text/css" href="t2matchlog.css">
628 <title>$title ($mission_name / $mission_type_name)</title>
629 </head>
630
631 <body>
632 <h1>$title <em class="mission">($mission_name / $mission_type_name)</em></h1>
633
634 END
635 return $html;
636 }
637
638 sub save_html {
639 my $file = shift;
640 my $html = shift;
641 $html .= "</body></html>\n";
642 open HTML, ">$outdir/$file" or die "failed to open $outdir/$file: $!";
643 print HTML $html;
644 close HTML;
645 }
646
647 ####################################################################################################
648
649 sub safe_name {
650 my $s = shift;
651 $s =~ tr/[a-zA-Z0-9]/_/cs;
652 return $s;
653 }
654
655 sub nice_time {
656 my $time = shift;
657 my $secs = $time % 60;
658 return int($time / 60) . ':' . (($secs < 10) ? "0$secs" : $secs);
659 }
660
661 sub distance {
662 return sqrt (($_[0] - $_[3]) ** 2 + ($_[1] - $_[4]) ** 2 + ($_[2] - $_[5]) ** 2);
663 }
664
665 ####################################################################################################
666

  ViewVC Help
Powered by ViewVC 1.1.26