X-Git-Url: http://git.shiar.nl/perl/schtarr.git/blobdiff_plain/4e5d2696326f3562419f2fa5c3b2f81611c6af49..d4581a2bec64edc2357aef32345819ee2917b279:/Data-StarCraft/lib/Data/StarCraft/Tileset.pm diff --git a/Data-StarCraft/lib/Data/StarCraft/Tileset.pm b/Data-StarCraft/lib/Data/StarCraft/Tileset.pm index 0b17b07..891c12b 100644 --- a/Data-StarCraft/lib/Data/StarCraft/Tileset.pm +++ b/Data-StarCraft/lib/Data/StarCraft/Tileset.pm @@ -4,42 +4,216 @@ use strict; use warnings; use Data::Dumper; +use List::Util qw(sum); + +our $VERSION = '0.10'; + sub open { my ($class, $filename) = @_; my $self = bless {}, $class; open my $groupfile, '<', "$filename.cv5" or return; $self->readgroups($groupfile); - # vf4 = minitile type - # vr4 = minitile sprites - # vx4 = minitile map? + if (open my $groupfile, '<', "$filename.vx4") { + $self->readvx4($groupfile); + } + if (open my $groupfile, '<', "$filename.vf4") { + $self->readvf4($groupfile); + } + if (open my $groupfile, '<', "$filename.vr4") { + $self->readvr4($groupfile); + } + if (open my $groupfile, '<', "$filename.wpe") { + $self->readwpe($groupfile); + } return $self; } sub _read { my $self = shift; my ($fh, $size, $seek) = @_; + seek $fh, $seek, 0 if $seek; read($fh, my $in, $size) eq $size or return undef; return $in; } +sub readwpe { + my $self = shift; + my ($fh) = @_; + + # wpe = palette + while (defined (my $line = $self->_read($fh, 4))) { + push @{ $self->{palette} }, [ unpack 'CCCX', $line ]; + } + return; +} + +sub readvx4 { + my $self = shift; + my ($fh) = @_; + + # vx4 = minitile map (4x4 references per megatile) + while (defined (my $line = $self->_read($fh, 16*2))) { + push @{ $self->{tileref} }, [ unpack 'v*', $line ]; + } + return; +} + +sub readvr4 { + my $self = shift; + my ($fh) = @_; + + # vr4 = minitile sprites + while (defined (my $line = $self->_read($fh, 8**2))) { + push @{ $self->{minibmp} }, [ unpack 'C*', $line ]; + } + return; +} + +sub readvf4 { + my $self = shift; + my ($fh) = @_; + + # vf4 = minitile type + while (defined (my $line = $self->_read($fh, 2))) { + push @{ $self->{minitype} }, unpack 'v', $line; + } + return; +} + +sub col { + my $self = shift; + my ($minitile) = @_; + + return $self->{bmp}->[$minitile] if defined $self->{bmp}->[$minitile]; + + my $pixels = $self->{minibmp}->[$minitile]; + my $pal = $self->{palette}; + + my @rgb; + for my $color (@$pixels) { +# $rgb[$_] += $self->{palette}->[$color]->[$_] for 0 .. 2; +# # ^ wow, this is really slow + $rgb[0] += $pal->[$color]->[0]; + $rgb[1] += $pal->[$color]->[1]; + $rgb[2] += $pal->[$color]->[2]; + } + return $self->{bmp}->[$minitile] = [map {$_ >> 6} @rgb]; +} + +=head2 col + +Average color [r,g,b] for minitile. + +For example, using the Jungle tileset: + + $t->col(719) + +is a water minitile, giving a blue color C<[38, 38, 57]>. + +=cut + +sub sprite { + my $self = shift; + my ($id) = @_; + + my $minitiles = $self->{tileref}->[$id]; + my @pix; + for my $tiley (0 .. 3) { + for my $y (0 .. 7) { + for my $tilex (0 .. 3) { + # minitile number is half of tileref + # if odd, the minitile is mirrored + my $minitile = $minitiles->[$tilex + $tiley*4]; + + for my $x ($minitile & 1 ? (reverse 0 .. 7) : (0 .. 7)) { + push @pix, $self->{minibmp}->[$minitile >> 1]->[$x + $y*8]; + } + } + } + } + return \@pix; +} + +=head2 sprite + +Returns bitmap of an entire tile, as 32x32 color indexes. + +For example, the green value of the bottom center pixel of tile #1 would be: + + $t->{palette}->[ $t->sprite(1)->[31*32 + 15] ]->[2] + +=cut + +sub tile { + my $self = shift; + my ($id) = @_; + + my $tile = $self->{map}->[$id]; + my $minitiles = $self->{tileref}->[$tile] || []; + return { + id => $tile, + group => $self->{group}->[$id >> 4], +# subtype => [ map { $self->{minitype}->[$_] } @$minitiles ], + subtype => [ map { $self->{minitype}->[$_] } $tile*16 .. $tile*16+15 ], + subcol => [ map { $self->col($_ >> 1) } @$minitiles ], +# sprite => $self->sprite($tile), + }; +} + +sub tileavg { + my $self = shift; + my $tile = $self->tile(shift); + + my $info = $tile->{group}; + $info->{walk} = sum(@{ $tile->{subtype} }) >> 4; + my @rgb; + for my $subcol (@{ $tile->{subcol} }) { + $rgb[$_] += $subcol->[$_] for 0 .. 2; + } + $info->{col} = [ map {$_ >> 4} @rgb ]; + return $info; +} + +=head2 tile + +Tile details. For example a water tile: + + $t->tile(96) + +would give: + + group => { + walk => 0, + height => 0, + build => 128, + }, + subtype => [ 1,1,1,1, 1,1,1,1, 1,1,1,1, 1,1,1,1 ], + subcol => [ + [39,40,59], [38,39,57], [40,41,60], [36,37,55], + [37,38,56], [39,40,60], [38,39,57], [40,41,61], + [37,38,56], [41,41,61], [40,40,60], [36,36,54], + [36,36,54], [37,37,55], [35,35,53], [38,38,57] + ], + +=cut + sub readgroups { my $self = shift; my ($fh) = @_; - my $id = 0; + + # cv5 = tile groups my @tilemap; while (defined (my $line = $self->_read($fh, 52))) { my @data = unpack "vCC v24", $line; my %row = ( build => $data[1] & 0xF0, height => $data[2] & 0xF, - walk => undef, +# rawdata => [@data[0..10]], ); - $self->{group}->[$id] = \%row; - push @tilemap, $_ for @data[11..26]; - $id++; + push @{ $self->{group} }, \%row; + push @{ $self->{map} }, $_ for @data[11..26]; } - $self->{tile} = \@tilemap; return; # index (ground_height unknown4 buildable unknown3) u6 u7 u8 u9 u10 u11 u12 u13 # 1 (ground_height unknown4 buildable unknown3) 35 0 doodad_group 0 58 6 6 0