X-Git-Url: http://git.shiar.nl/sheet.git/blobdiff_plain/f00c1e5c4de7325c9d5d851a61355fe8638cca95..29b407ddf91b653e6e8380f73e221628a392376f:/sc.plp diff --git a/sc.plp b/sc.plp index f1632ee..b5d81ec 100644 --- a/sc.plp +++ b/sc.plp @@ -9,7 +9,7 @@ my %scver = ( major => 1, ); -if ($ENV{PATH_INFO} and $ENV{PATH_INFO} eq '/2') { +if ($Request and $Request eq '2') { %scver = ( id => 'hots', name => 'Heart of the Swarm', @@ -22,7 +22,7 @@ my $datafile = "sc-units-$scver{id}.inc.pl"; Html({ title => "$scver{title} unit cheat sheet", - version => 'v1.1', + version => '1.1', description => [ "Reference of $scver{game} unit properties," . " comparing various statistics of all the units in $scver{name}" @@ -40,19 +40,19 @@ Html({ data => [$datafile], }); -print "

$scver{game} units

\n\n"; +say "

$scver{game} units

\n"; my $units = do $datafile; die "Cannot open unit data: $_\n" for $@ || $! || (); my $patch = shift @{$units} or die "Cannot open unit data: metadata not found\n"; -print "

Unit properties as seen or measured in $scver{name}\n$patch.\n"; -print "Also see the $_ table.\n" for join(', ', +say "

Unit properties as seen or measured in $scver{name}\n$patch."; +say "Also see the $_ table." for join(', ', ('StarCraft 2: HotS') x ($scver{major} < 2), ('original SC: Brood War') x ($scver{major} > 1), ); -print "

\n\n"; +say "

\n"; sub addupgrade { my ($ref, $increase, $org) = @_; @@ -95,10 +95,11 @@ sub coltoggle { <:= coltoggle('name', '') :> - min - gas + cost + gas <:= coltoggle(qw'build cost') :> - <:= coltoggle(qw'size size') :> + <:= coltoggle(qw'size size') :> + attr HP shield ⛨ @@ -113,11 +114,33 @@ sub coltoggle { sub showrange { my ($min, $max) = @_; return '' if not defined $min; - $_ &&= int($_ + .5) for $min, $max; # round halves up - return $min if not defined $max or $min == $max; + return $min || '-' if !$max or $min == $max; return "$min-$max"; } +sub showrangeint { + $_ &&= int($_ + .5) for @_; # round halves up + return showrange(@_); +} + + sub showcost { + my ($row, $unit) = @_; + return join(' ', + sprintf('cost %s%%', join '-', + map { $_ && sprintf '%.0f', 100 * $row->{cost} / $_ } grep { defined $_ } + $unit->{energy}, + $unit->{upgraded}->{energy}, + $unit->{capacity}, + $unit->{upgraded}->{capacity}, + ), + !defined $row->{maint} ? () : sprintf('+%s%%/s', join '-', + map { sprintf '%.1f', 100 * $row->{maint} / $_ } grep $_, + $unit->{capacity}, + $unit->{upgraded}->{capacity}, + ), + ); + } + sub showattack { my ($row, $area) = @_; my $attack = $row->{attack}->[$area] @@ -126,38 +149,44 @@ sub showrange { my $upattack = $row->{upgraded}->{attack}->[$area]; my $damage = $attack->{damage}; my $maxdamage = $upattack->{damage} // $damage; - $damage = $damage->[0] if ref $damage; - $maxdamage = $maxdamage->[-1] if ref $maxdamage; + $maxdamage += ($upattack->{upgrade} // $attack->{upgrade}) * 3; my $out = ''; - $out .= "$attack->{count}× " if $attack->{count} > 1; + $out .= sprintf '¤ ', showcost($attack, $row) + if $attack->{cost}; + $out .= sprintf('%s× ', + showrangeint($attack->{count}, $upattack->{count}), + ) if $attack->{count} > 1; $out .= '*' if $attack->{type} eq 'explosive'; $out .= '~' if $attack->{type} eq 'implosive'; + if (my @bonus = sort grep { !/^-/ } keys %{ $attack->{bonus} }) { $out .= sprintf('', (map { - $_ =~ /^light/ ? 'unit-s' : + $_ eq 'light' ? 'unit-s' : $_ eq 'armored' ? 'unit-l' : $_ eq 'organic' ? 'unit-o' : - $_ =~ /^massive/ ? 'unit-h' : + $_ eq 'massive' ? 'unit-h' : $_ eq 'shields' ? 'unit-shield' : '', - } join '_', keys %{ $attack->{bonus} }), + } join '_', @bonus), join(', ', map {( sprintf('+%s vs %s', - (map { - ref $_ ? showrange($_->[0], $_->[-1]) : $_ - } $attack->{bonus}->{$_}), + showrangeint( + $attack->{bonus}->{$_}, + $attack->{bonus}->{$_} + $attack->{bonus}->{"-$_"} * 3, + ), $_, ), - )} keys %{ $attack->{bonus} }), - ) if $attack->{bonus}; + )} @bonus), + ); + } $out .= '•' if $attack->{type} eq 'projectile'; $out .= sprintf '', $attack->{name} if $attack->{name}; - $out .= showrange($damage, $maxdamage); + $out .= showrangeint($damage, $maxdamage); $out .= '' if $attack->{name}; $out .= sprintf('%s', $attack->{splash} eq 'line' ? ('linear', '+') : ('splash', '⁜') @@ -165,7 +194,10 @@ sub showrange { $out .= ''; if ($attack->{dps}) { - $out .= showrange($attack->{dps}->[0], $attack->{dps}->[-1]); + # precalculated dps, do not touch + $out .= showrangeint($attack->{dps}->[0], + $upattack->{dps}->[-1] // $attack->{dps}->[-1] + ); } elsif ($attack->{cooldown}) { if (my $type = $attack->{type}) { @@ -179,19 +211,20 @@ sub showrange { $damage *= ($attack->{count} // 1) / $attack->{cooldown}; if (my $bonus = $upattack->{bonus} // $attack->{bonus}) { $maxdamage += $_ for max( - map { ref $_ ? $_->[-1] : $_ } values %{$bonus} + map { $bonus->{$_} + $bonus->{"-$_"} * 3 } + grep { !/^-/ } keys %{$bonus} ); } $maxdamage *= ($upattack->{count} // $attack->{count} // 1) / ($upattack->{cooldown} // $attack->{cooldown}); - $out .= showrange($damage, $maxdamage); + $out .= showrangeint($damage, $maxdamage); } $out .= '' . '▽' x !!($attack->{anti} & 1); $out .= '' . '△' x !!($attack->{anti} & 2); $out .= '' . - showrange($attack->{range}, $upattack->{range}); + showrangeint($attack->{range}, $upattack->{range}); return $out; } @@ -208,12 +241,7 @@ sub showrange { (map { $_ && " ($_)" } join ', ', #TODO: apply upgrades $_->{range} ? "range $_->{range}" : (), - $_->{cost} ? sprintf('cost %.0f%%%s', - 100 * $_->{cost} / $row->{energy}, - defined $_->{maint} && sprintf('+%.1f%%/s', - 100 * $_->{maint} / $row->{energy}, - ), - ) : + $_->{cost} ? showcost($_, $row) : $_->{cooldown} ? "cooldown $_->{cooldown}s" : (), ), ), @@ -225,25 +253,6 @@ sub showrange { my ($row) = @_; local $_ = $row; $_->{hp} += $_->{shield} if $_->{shield}; - my $suitchar = ''; - if ($_->{attr}->{structure}) { - $suitchar = 'b'; - } - elsif ($_->{suit}) { - $suitchar = [qw/? s m l/]->[$_->{suit}]; - } - elsif ($_->{cargo} > 0) { - $suitchar = [qw/? s m l l h h h h/]->[abs $_->{cargo}]; - } - elsif ($_->{size}) { - $suitchar = [qw/s m l h h h/]->[$_->{size}]; - } - elsif ($_->{attr} and $_->{attr}->{light}) { - $suitchar = 's'; - } - elsif ($_->{attr} and $_->{attr}->{armored}) { - $suitchar = 'l'; - } return ( '' . ($_->{min} // ''), @@ -252,13 +261,21 @@ sub showrange { !!$_->{base} && '+', $_->{build} || '0', ), - !$suitchar ? '' : sprintf('%s%s', - $suitchar, ucfirst $suitchar, - $_->{attr}->{massive} - && '⚓', + sprintf('%s', + $_-> {cargo} < 0 ? ('supply', T => 'transport') : + $_->{upgraded}->{cargo} < 0 ? ('supply magic-opt', T => 'optional transport') : + $_->{attr}->{flying} ? ('air', F => 'flying') : + $_->{attr}->{structure} ? ('x', B => 'building') : + ( + [qw( x s m l l h h h h )]->[ $_->{cargo} ], + $_->{cargo} || '-', + $_->{cargo} ? 'transportable' : 'untransportable', + ), + defined $_->{size} && sprintf('⌀%.1f ', $_->{size}), ), - '' . ( - defined $_->{unit} && $_->{unit} == .5 ? '½' : $_->{unit} + sprintf('%s', + defined $_->{pop} && $_->{pop} < 0 && ' unit-supply', + defined $_->{pop} && $_->{pop} == .5 ? '½' : $_->{pop}, ), '' . join('', grep { $_ } (defined $_->{organic} ? !$_->{organic} : $_->{attr}->{mech}) @@ -273,24 +290,45 @@ sub showrange { && 'A', $_->{attr}->{light} && 'L', + $_->{suit} && sprintf( + '%s', + map { @{$_} } [ + [qw( x ? unknown )], + [qw( s S small )], + [qw( m M medium )], + [qw( l L large )], + ]->[ $_->{suit} ], + ), + $_->{attr}->{massive} + && '⚓', ), '' . $_->{hp} // '', $_->{shield} ? sprintf('%.0f%%{shield} / $_->{hp} ) : '' . - showrange($_->{armor}, $_->{upgraded}->{armor}), + showrangeint($_->{armor}, $_->{upgraded}->{armor}), showattack($_, 0), '' . sprintf( $_->{detect} ? '%s' : '%s', - showrange($_->{sight}, $_->{upgraded}->{sight}) + showrangeint($_->{sight}, $_->{upgraded}->{sight}) + ), + sprintf('%s', + showrange( + map { $_ && sprintf '%.1f', $_ } + $_->{speed}, $_->{upgraded}->{speed} + ), + defined $_->{creep} && sprintf(' title="%s on creep"', + $_->{creep} == 1 ? 'same' : showrange( + map { $_ && sprintf '%.1f', $_ } + $_->{speed} * $_->{creep}, + $_->{upgraded}->{speed} && $_->{upgraded}->{speed} * + ($_->{upgraded}->{creep} // $_->{creep}), + ), + ), ), - '' . - showrange($_->{speed}, $_->{upgraded}->{speed}), $_->{attr}->{jump} && qq'↕', - $_->{attr}->{flying} - && qq'↑', '' . showmagic($_), !$_->{attack}->[1] ? () : ( '', showattack($_, 1), '' @@ -308,19 +346,19 @@ sub showrange { $get{order} ||= ''; if ($get{order} eq 'size') { $_->{order} = ( - $_->{unit}*16 + ($_->{size} // $_->{suit}) + $_->{cargo}/8 + $_->{pop}*16 + ($_->{size} // $_->{suit}) + $_->{cargo}/8 + $_->{hp}/512 + $_->{min}/8192 ) for @$units; } elsif ($get{order} eq 'cost') { $_->{order} = ( - $_->{gas}*1.5 + $_->{min} + $_->{unit}/8 + $_->{build}/256/8 + $_->{gas}*1.5 + $_->{min} + $_->{pop}/8 + $_->{build}/256/8 ) for @$units; } elsif ($get{order} eq 'attack') { $_->{order} = $_->{hp} / 1024 + $_->{shield} / 1008 + max( map { - ((map { ref $_ ? $_->[-1] : $_ } $_->{damage})[0]) + ($_->{damage} + $_->{upgrade} * 3) * ($_->{count} // 1) / ($_->{cooldown} // 1) * ($_->{splash} ? 1.01 : 1) * ($_->{type} eq 'implosive' ? .96 : 1) @@ -338,7 +376,7 @@ sub showrange { my ($race, $cat) = ('', ''); for (@rows) { if ($grouped) { - printf '

%s

'."\n", + say sprintf '

%s

', $race = $_->{race}, ucfirst $race unless $race eq $_->{race}; } @@ -369,27 +407,16 @@ sub showrange {
cost -
minerals+gas required to create one unit +
minerals and gas required to create one unit
includes total expenses if based on existing units
build
relative time needed to create at least one unit
excludes construction of dependencies such as buildings and +parent units
size -
<: -if ($scver{major} > 1) { - :>transports can fit 8 Small, - 4 Medium, - 2 Large, - or a single Huge unit -
massive ⚓ units - cannot be lifted or slowed and can break force fields<: -} else { - :>affected by Small, - Medium, or - Large unit damage<: -} :> -
number of command points taken per unit +
Transports can fit upto + 8 non-Flying cargo units +
number of command points taken while alive
<: if ($scver{major} > 1) { :>received damage depends on @@ -398,16 +425,35 @@ if ($scver{major} > 1) { ψ(ps)ionic, Light, and Armored - attributes<: + attributes +
massive ⚓ units + cannot be lifted or slowed and can break force fields<: } else { - :>organic/mechanic unit<: + :>abilities may hit only organic + or mechanic targets +
affected by Small, + Medium, or + Large damage<: } :> -
HP
- total number of hitpoints (including shields) +
HP +
total number of hitpoints (including shields) +
everything zerg (except for eggs) regenerates one point every + <:= $scver{major} == 1 ? '4½' : '3.7' :> seconds
shield
percentage of HP in shields -
shields always take full damage, irrelevant of unit size -
does not take armor bonuses, but upgrades can decrease damage to any shield hit by upto 3 +
<: +if ($scver{major} > 1) { + :>shields always take full damage, irrelevant of unit size +
<: +} + :>does not take armor bonuses, + but upgrades can decrease damage to any shield hit by upto 3 +
<: +if ($scver{major} > 1) { + :>after 10 seconds out of combat, 2 points are recharged per game second<: +} else { + :>recharges one point every 2½ seconds<: +} :>
armor
base unit armor
can be increased by upto 3 at various facilities @@ -415,9 +461,8 @@ if ($scver{major} > 1) {
reduction applies to initial damage, before size penalties (so a plasma hit of 12 to 4 armor large deals 2 damage, not ½)
attack -
damage given per single hit -
dps indicates relative amount of damage - done in 1 second of in-game time +
damage per single hit +
some weapons fire multiple × times, multiplying armor penalties
splash damage hits all objects nearby ⁜ or in a straight line +.
<: @@ -428,30 +473,38 @@ if ($scver{major} > 1) { Point Defense Drones<: } else { :>*explosive damage does only - 50% damage to small units, 75% to medium, 100% to large + 50% damage to Small units, + 75% to Medium, + 100% to Large
~concussive/plasma damage does - 25% to large, 50% medium, 100% to small units<: + 25% to Large, + 50% Medium, + 100% to Small units<: } :> +
dps indicates relative total amount of damage + done in 1 second of <:= $scver{major} > 1 ? 'Normal in-game time' : + 'time on Fast game speed' :>
targets ▽ ground and/or △ air
range -
maximum range of weapon (note siege tank also has a minimum range) +
maximum hex distance a weapon can fire (note Sieged Tank also has a minimum)
sight
range in which the unit detects other units
emphasis indicates ability to detect cloaked units
speed -
relative speed of movement (when in full motion, startup speed ignored) +
top movement speed in hex per second +
acceleration and deceleration ignored
specials
parentheses () indicate that it needs to be researched first
passive abilities are always enabled
hover for description -
range is maximum range required to activate -
cost is percentage of total energy lost +
range is maximum distance allowed to activate +
cost describes energy loss percentage on spawn and when fully charged

-When two values are given (1-2), second value indicates attribute after all -possible upgrades. +When two values are given (1-2), the second value indicates the attribute +after all possible upgrades.