<(common.inc.plp)><:
use List::Util qw(max sum);
-my %scver = (
- id => 'bw',
- name => 'Brood War',
- title => 'starcraft',
- game => 'StarCraft',
- major => 1,
-);
-
-if ($ENV{PATH_INFO} and $ENV{PATH_INFO} eq '/2') {
- %scver = (
- id => 'hots',
+my %scvers = (
+ bw => {
+ name => 'Brood War',
+ title => 'starcraft',
+ game => 'StarCraft BW',
+ major => 1,
+ },
+ hots => {
name => 'Heart of the Swarm',
- title => 'starcraft2',
- game => 'StarCraft II',
+ title => 'starcraft2 hots',
+ game => 'StarCraft II HotS',
major => 2,
- );
-}
-my $datafile = "sc-units-$scver{id}.inc.pl";
+ },
+ lotv => {
+ name => 'Legacy of the Void',
+ title => 'starcraft2 lotv',
+ game => 'StarCraft II LotV',
+ major => 2,
+ },
+);
+
+$Request ||= 'bw';
+$Request = 'lotv' if $Request eq '2';
+my %scver = %{ $scvers{$Request} }
+ or Abort("Requested version $Request not available", '404 unknown');
+
+my $datafile = "sc-units-$Request.inc.pl";
Html({
title => "$scver{title} unit cheat sheet",
- version => 'v1.1',
+ version => '1.2',
description => [
"Reference of $scver{game} unit properties,"
. " comparing various statistics of all the units in $scver{name}"
keywords => [
qw'
starcraft game unit statistics stats comparison table sheet cheat
- reference software attributes properties
+ reference software attributes properties patch attribute multiplayer
',
- $scver{major} < 2 ? qw' bw broodwar brood war ' : qw' starcraft2 hots ',
+ $scver{major} < 2 ? qw' bw broodwar brood war ' :
+ qw' starcraft2 lotv hots wol ',
],
- stylesheet => [qw'light'],
- raw => '<link rel="stylesheet" type="text/css" media="all" href="/sc.css?1.1" title="light">',
+ stylesheet => [qw( light dark )],
+ raw => '<link rel="stylesheet" type="text/css" media="all" href="/sc.css?1.2">',
data => [$datafile],
});
-print "<h1>$scver{game} units</h1>\n\n";
+say "<h1>$scver{game} units</h1>\n";
my $units = do $datafile;
-die "Cannot open unit data: $_\n" for $@ || $! || ();
+Abort("Cannot open unit data", 501, $_) for $@ || $! || ();
my $patch = shift @{$units}
- or die "Cannot open unit data: metadata not found\n";
+ or Abort("Cannot open unit data: metadata not found", 501);
-print "<p>Unit properties as seen or measured in $scver{name}\n$patch.\n";
-print "Also see the $_ table.\n" for join(', ',
- ('<a href="/sc/2">StarCraft 2: HotS</a>') x ($scver{major} < 2),
- ('<a href="/sc">original SC: Brood War</a>') x ($scver{major} > 1),
+say "<p>Unit properties as seen or measured in $scver{name}\n$patch.";
+say "Also see the $_ tables." for join(' and ',
+ (showlink('StarCraft 2: LotV', '/sc/lotv')) x ($Request ne 'lotv'),
+ (showlink( 'HotS', '/sc/hots')) x ($Request ne 'hots'),
+ (showlink('original SC: Brood War', '/sc/bw')) x ($Request ne 'bw'),
);
-print "</p>\n\n";
+say "</p>\n";
sub addupgrade {
my ($ref, $increase, $org) = @_;
sub coltoggle {
my ($name, $id, $nolink) = @_;
- return sprintf(
- (defined $get{order} ? $get{order} eq $id : !$id) ? '%2$s ▼'
- : $nolink ? '%2$s' : '<a href="?%s">%s</a>',
- $id && "order=$id", $name
- );
+ return "$name ▼" if defined $get{order} ? $get{order} eq $id : !$id;
+ return $name if $nolink;
+ return showlink($name, '?'.($id && "order=$id"));
}
:><table class="units">
<thead><tr>
- <th></th>
- <th><:= coltoggle('name', '') :></th>
- <th class="val min" title=minerals>cost</th>
- <th class="val gas">gas</th>
- <th class="val time"><:= coltoggle(qw'build cost') :></th>
+ <th><:= coltoggle(exists $get{order} ? 'race' : 'source' => '') :></th>
+ <th class="unit-name"><:= coltoggle(name => 'name') :></th>
+ <th class="val unit-min" title=minerals><:= coltoggle(cost => 'cost') :></th>
+ <th class="val unit-gas">gas</th>
+ <th class="val time"><:= coltoggle(build => 'build') :></th>
<th class="unit" colspan="2"><:= coltoggle(qw'size size') :></th>
- <th class="unit" colspan="2">attr</th>
- <th class="val unit-hp">HP</th>
+ <th class="unit unit-attr" colspan="2">attr</th>
+ <th class="val unit-hp"><:= coltoggle(HP => 'hp') :></th>
<th class="val unit-shield">shield</th>
<th class="val unit-armor" title="armor">⛨</th>
- <th class="val hurt">attack</th>
- <th class="hurt hurtrel"><:= coltoggle(qw'dps attack 1') :></th>
+ <th class="val hurt"><:= coltoggle(attack => 'attack') :></th>
+ <th class="hurt hurtrel">dps</th>
<th class="val unit-range" colspan=3>range</th>
<th class="val unit-sight">sight</th>
- <th class="val unit-speed">speed</th>
+ <th class="val unit-speed"><:= coltoggle(speed => 'speed') :></th>
<th class="unit-magic">specials</th>
</tr></thead>
<:
$_->{hp} += $_->{shield} if $_->{shield};
return (
- '<td class="val min">' . ($_->{min} // ''),
- '<td class="val gas">' . ($_->{gas} || ''),
- !defined $_->{build} ? '<td>' : sprintf('<td class="val time">%s%.0f',
- !!$_->{base} && '<span class="unit-composed">+</span>',
- $_->{build} || '0',
+ '<td class="val unit-min">' . ($_->{min} // ''),
+ '<td class="val unit-gas">' . ($_->{gas} || ''),
+ defined $_->{transform} ? sprintf('<td class="val time">%.0f',
+ $_->{transform},
+ ) :
+ !defined $_->{build} ? '<td>' : sprintf('<td class="val time"%s>%s%.0f',
+ defined $_->{warp} && qq(title="$_->{build} without warpgate"),
+ !!$_->{base} && sprintf(
+ '<span class="unit-composed" title="%s">+</span>',
+ 'from '.join('+', @{ $_->{base} }),
+ ),
+ $_->{warp} // $_->{build} || '0',
),
sprintf('<td class="unit unit-%s" title="%4$s%3$s">%s',
$_-> {cargo} < 0 ? ('supply', T => 'transport') :
$_->{upgraded}->{cargo} < 0 ? ('supply magic-opt', T => 'optional transport') :
- $_->{attr}->{flying} ? ('air', F => 'flying') :
+ $_-> {attr}->{flying} ? ('air', F => 'flying') :
+ $_->{upgraded}->{attr}->{flying} ? ('air magic-opt', F => 'potentially flying') :
$_->{attr}->{structure} ? ('x', B => 'building') :
(
[qw( x s m l l h h h h )]->[ $_->{cargo} ],
),
defined $_->{size} && sprintf('⌀%.1f ', $_->{size}),
),
- sprintf('<td class="val unit%s">%s',
+ sprintf('<td class="val unit unit-pop%s">%s',
defined $_->{pop} && $_->{pop} < 0 && ' unit-supply',
defined $_->{pop} && $_->{pop} == .5 ? '½' : $_->{pop},
),
$_->{attr}->{massive}
&& '<span class="unit-massive" title="massive">⚓</span>',
),
- '<td class="val unit-hp">' . $_->{hp} // '',
+ $_->{hp} < 0 ? '<td class="val unit-hp" title="invulnerable">∞' :
+ '<td class="val unit-hp">' . showrangeint($_->{hp}, $_->{upgraded}->{hp}),
$_->{shield} ? sprintf('<td class="val unit-shield">%.0f%%<td',
100 * $_->{shield} / $_->{hp}
) : '<td colspan=2',
&& qq'<span class="unit unit-jump" title="$_->{attr}->{jump}">↕</span>',
'<td class="unit-magic">' . showmagic($_),
!$_->{attack}->[1] ? () : (
- '<tr><td colspan=12>', showattack($_, 1), '<td colspan=3>'
+ '<tr><th class="cat" colspan="2"><td colspan=10>', showattack($_, 1), '<td colspan=3>'
),
!$_->{attack}->[2] ? () : (
- '<tr><td colspan=12>', showattack($_, 2), '<td colspan=3>'
+ '<tr><th class="cat" colspan="2"><td colspan=10>', showattack($_, 2), '<td colspan=3>'
),
"\n"
);
}
+ my @rows = @{$units};
my $grouped = 1; # race headers
if (exists $get{order}) {
$grouped = 0;
$get{order} ||= '';
- if ($get{order} eq 'size') {
+ if ($get{order} eq 'name') {
+ @rows = sort {$a->{name} cmp $b->{name}} @rows;
+ }
+ elsif ($get{order} eq 'hp') {
$_->{order} = (
- $_->{pop}*16 + ($_->{size} // $_->{suit}) + $_->{cargo}/8
- + $_->{hp}/512 + $_->{min}/8192
- ) for @$units;
+ $_->{hp}*1.01 + $_->{armor} + $_->{shield} + $_->{size}/1024,
+ ) for @rows;
}
elsif ($get{order} eq 'cost') {
$_->{order} = (
$_->{gas}*1.5 + $_->{min} + $_->{pop}/8 + $_->{build}/256/8
- ) for @$units;
+ ) for @rows;
+ }
+ elsif ($get{order} eq 'build') {
+ my %unittime = map { ($_->{name} => $_->{warp} // $_->{build}) } @rows;
+ $unittime{Templar} = $unittime{'High Templar'};
+ $_->{order} = (
+ ($_->{warp} // $_->{build})
+ + ($_->{gas}*1.5 + $_->{min} + $_->{pop}/8)/1024
+ + ($_->{base} ? ($unittime{$_->{base}->[0]} // 100) + 1 : 0)
+ ) for @rows;
+ }
+ elsif ($get{order} eq 'size') {
+ $_->{order} = (
+ $_->{pop}*16 + ($_->{size} // $_->{suit}) + $_->{cargo}/8
+ + $_->{hp}/512 + $_->{min}/8192
+ ) for @rows;
}
elsif ($get{order} eq 'attack') {
$_->{order} = $_->{hp} / 1024 + $_->{shield} / 1008 + max(
map {
- ($_->{damage} + $_->{upgrade} * 3)
- * ($_->{count} // 1) / ($_->{cooldown} // 1)
+ ($_->{dps} ? $_->{dps}->[-1] :
+ ($_->{damage} + $_->{upgrade} * 3)
+ * ($_->{count} // 1) / ($_->{cooldown} // 1)
+ )
* ($_->{splash} ? 1.01 : 1)
* ($_->{type} eq 'implosive' ? .96 : 1)
* ($_->{type} eq 'explosive' ? .98 : 1)
} @{ $_->{attack} }
- ) for @$units;
+ ) for @rows;
}
- else {
- $units->[$_]->{order} = $_ for 0 .. $#$units;
+ elsif ($get{order} eq 'speed') {
+ $_->{order} = (
+ ($_->{upgraded}->{speed} // $_->{speed}*1.01)
+ + $_->{sight}/1024 + $_->{detect}/2048
+ ) for @rows;
}
+ @rows = sort {$a->{order} <=> $b->{order}} @rows if exists $rows[0]->{order};
}
- my @rows = @{$units};
- @rows = sort {$a->{order} <=> $b->{order}} @rows unless $grouped;
my ($race, $cat) = ('', '');
for (@rows) {
if ($grouped) {
- printf '<tbody id="%s"><tr class="race"><th colspan="20"><h2>%s</h2>'."\n",
+ say sprintf '<tbody id="%s"><tr class="race"><th colspan="20"><h2>%s</h2>',
$race = $_->{race}, ucfirst $race
unless $race eq $_->{race};
}
<dl>
<dt>cost
- <dd>minerals and gas required to create one unit
+ <dd><span class="unit-min">minerals</span> and
+ <span class="unit-gas">gas</span> required to create one unit
<dd>includes total expenses if based on existing units
<dt>build
<dd>relative time needed to create at least one unit