<(common.inc.plp)><: my $datafile = 'sc-units.inc.pl'; if ($ENV{PATH_INFO} and $ENV{PATH_INFO} eq '/2') { $datafile = 'sc2-units.inc.pl'; } Html({ title => 'starcraft unit cheat sheet', version => 'v1.0', description => [ 'Reference of StarCraft unit properties,' . ' comparing various statistics of all the units in Brood War' . ' including costs, damage, defense, speed, ranges, and abilities.', ], keywords => [qw' starcraft game unit statistics stats comparison table sheet cheat reference software attributes properties '], stylesheet => [qw'light'], raw => '', data => [$datafile], }); :>

StarCraft units

Unit properties as seen or measured in Brood War version≥1.08.

<: sub coltoggle { my ($name, $id) = @_; return sprintf( (defined $get{order} ? $get{order} eq $id : !$id) ? '%2$s ▼' : '%s', $id && "order=$id", $name ); } :> <: sub showrange { my ($min, $max) = @_; $_ = int($_ + .5) for $min, $max; # round halves up return $min == $max ? $min : "$min-$max"; } sub showval { my ($row, @elements) = @_; my ($min, $max); my $value = $row; $value = ref $value eq 'HASH' && $value->{$_} or last for @elements; if (ref $value eq 'ARRAY') { $min = $value->[0]; $max = $value->[-1]; } else { $min = $max = $value; } defined $min or return ''; if ($row->{upgrade}) { for (@{ $row->{upgrade} }) { my $increase = $_ or next; $increase = ref $increase eq 'HASH' && $increase->{$_} or last for @elements; $increase = $increase->[-1] if ref $increase eq 'ARRAY'; $max += $increase if $increase; } } if ($elements[0] eq 'attack' and $elements[1] ne 'range' and $elements[2] eq 'dps') {{ my $attack = $row->{ $elements[0] }->{ $elements[1] }; ref $attack or $attack = $row->{ $elements[0] }->{$attack}; # follow my $type = $attack->{type} or next; if ($type eq 'explosive') { $min /= 2; } elsif ($type eq 'implosive') { $min /= 4; } }} return showrange($min, $max); } sub showattack { my ($row, $area) = @_; my $attack = $row->{attack}->{$area}; if (not ref $attack) { # reference to another area $area = $attack; $attack = $row->{attack}->{$area}; } return '', '
<:= coltoggle('name', '') :> min gas <:= coltoggle(qw'build cost') :> <:= coltoggle(qw'size size') :> HP shield armor ground air range sight speed specials
' unless $attack; my $tagbase = ''; my $out = showval($row, 'attack', $area, 'damage'); $out .= '+' if $attack->{splash}; $attack->{dps} = $attack->{cooldown} && [ map { 24 * $_ / $attack->{cooldown} * ($attack->{count} // 1) } map { ref $_ ? @{$_} : $_ } $attack->{damage} #TODO: upgrade (zergling) ]; $out .= '' . showval($row, 'attack', $area, 'dps'); return $tagbase . $out; } sub showmagic { my ($row) = @_; my $specials = $row->{special} or return ''; return join ' ', map { sprintf '%s', $_->{duration} < 0 && ' class="magic-perma"', join('', $_->{name}, $_->{desc} ? ": $_->{desc}" : '', $_->{range} || $_->{cost} ? sprintf(' (%s)', join ', ', $_->{range} ? "range $_->{range}" : (), $_->{cost} ? sprintf('cost %.0f%%', 100 * $_->{cost} / $row->{energy} ) : (), ) : '', ), sprintf($_->{build} ? '(%s)' : '%s', $_->{abbr}), } grep { defined $_->{abbr} } @{$specials}; } sub showunitcols { my ($row) = @_; local $_ = $row; $_->{hp} += $_->{shield} if $_->{shield}; my $suitchar = ''; if ($_->{suit}) { $suitchar = [qw/? s m l/]->[$_->{suit}]; } return ( '' . ($_->{min} // ''), '' . ($_->{gas} || ''), !defined $_->{build} ? '' : sprintf('%s%.0f', !!$_->{base} && '+', $_->{build} || '0', ), !$suitchar ? '' : sprintf('%s', $suitchar, ucfirst $suitchar), '' . join('', defined $_->{unit} && $_->{unit} == .5 ? '½' : $_->{unit}, defined $_->{organic} && sprintf( '%s', $_->{organic} ? 'o' : 'u', $_->{organic} ? 'organic' : 'mechanic', $_->{organic} ? 'o' : 'm', ), ), '' . $_->{hp} // '', '' . ( $_->{shield} ? sprintf('%.0f%%', 100 * $_->{shield} / $_->{hp}) : ' ' ), '' . showval($_, 'armor'), showattack($_, 'ground'), showattack($_, 'air'), '' . showval($_, 'attack', 'range'), '' . sprintf( $_->{detect} ? '%s' : '%s', showval($_, 'sight') ), '' . showval($_, 'speed'), '' . showmagic($_), "\n" ); } my $units = do $datafile; die "Cannot open unit data: $_\n" for $@ || $! || (); my $grouped = 1; # race headers if (exists $get{order}) { $grouped = 0; $get{order} ||= ''; if ($get{order} eq 'size') { $_->{order} = $_->{unit}*8 + $_->{suit} + $_->{hp}/512 + $_->{min}/8192 for @$units; } elsif ($get{order} eq 'cost') { $_->{order} = $_->{gas}*1.5 + $_->{min} + $_->{unit}/8 + $_->{build}/256/8 for @$units; } else { $units->[$_]->{order} = $_ for 0 .. $#$units; } } my @rows = @{$units}; @rows = sort {$a->{order} <=> $b->{order}} @rows unless $grouped; my ($race, $cat) = ('', ''); for (@rows) { if ($grouped) { printf '

%s

'."\n", $race = $_->{race}, ucfirst $race unless $race eq $_->{race}; } else { $_->{cat} = $_->{race}; } print( '
', $cat ne $_->{cat} && ($cat = $_->{cat}), '', $_->{name}, showunitcols($_), ); for my $subrow (@{ $_->{special} }) { $subrow->{alt} or next; print( '
', $subrow->{alt}, showunitcols($subrow), ); } } :>

Legend

cost
minerals+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
affected by Small, Medium, or Large unit damage
number of command points taken per unit
organic/mechanic unit
HP
total number of hitpoints (including shields)
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
armor
base unit armor
can be increased by upto 3 at various facilities
each point decreases damage per hit by one, upto a minimum of ½
reduction applies to initial damage, before size penalties (so a plasma hit of 12 to 4 armor large deals 2 damage, not ½)
ground/air
damage done per single attack against ground/air units
2nd column indicates relative amount of damage done in 1 second of fastest game time
splash damage+ hits nearby objects as well
explosive damage does only 50% damage to small units, 75% to medium, 100% to large
concussive/plasma damage does 25% to large, 50% medium, 100% to small units
sight
range in which the unit detects other units
emphasis indicates ability to detect cloaked units
range
maximum range of weapon (note siege tank also has a minimum range)
speed
relative speed of movement (when in full motion, startup speed ignored)
specials
special abilities are usually casted manually, but some are always active
parentheses () indicate that it needs to be researched first
hover for description
range is maximum range required to activate
cost is percentage of total energy lost

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