File Coverage

blib/lib/Game/LevelMap.pm
Criterion Covered Total %
statement 77 77 100.0
branch 14 14 100.0
condition 18 20 90.0
subroutine 13 13 100.0
pod 6 7 85.7
total 128 131 97.7


line stmt bran cond sub pod time code
1             # -*- Perl -*-
2             #
3             # a small level map implementation that uses characters (or strings
4             # consisting of escape sequences or combining characters, or objects
5             # that ideally stringify themselves properly) in an array of arrays
6             # representing a level map as might be used in a game
7              
8             package Game::LevelMap;
9              
10 2     2   66873 use 5.24.0;
  2         13  
11 2     2   11 use warnings;
  2         3  
  2         60  
12 2     2   10 use Carp qw(croak);
  2         3  
  2         148  
13 2     2   1076 use Moo;
  2         23123  
  2         13  
14 2     2   3750 use namespace::clean;
  2         23904  
  2         12  
15              
16             our $VERSION = '0.02';
17              
18             has level => (
19             is => 'rw',
20             default => sub { [ [] ] },
21             isa => sub {
22             my ($lm) = @_;
23             croak "LevelMap must be an AoA"
24             if !defined $lm
25             or ref $lm ne 'ARRAY'
26             or ref $lm->[0] ne 'ARRAY';
27             my $cols = $lm->[0]->$#*;
28             for my $row ( 1 .. $lm->$#* ) {
29             if ( $cols != $lm->[$row]->$#* ) {
30             croak 'unequal column length at row index ' . $row;
31             }
32             }
33             },
34             trigger => sub {
35             my ( $self, $lm ) = @_;
36             $self->_set_rows( $lm->$#* );
37             $self->_set_cols( $lm->[0]->$#* );
38             }
39             );
40             has rows => ( is => 'rwp' );
41             has cols => ( is => 'rwp' );
42              
43             sub BUILD {
44 8     8 0 65 my ( $self, $args ) = @_;
45             croak "level and from_string both may not be set"
46 8 100 100     35 if exists $args->{level} and exists $args->{from_string};
47 7 100       38 $self->from_string( $args->{from_string} ) if exists $args->{from_string};
48             }
49              
50             sub clone {
51 1     1 1 311 my ($self) = @_;
52 1         25 my $lm = $self->level;
53 1         7 my @map;
54 1         4 for my $rown ( 0 .. $lm->$#* ) {
55 3         8 for my $coln ( 0 .. $lm->$#* ) {
56 9         20 $map[$rown][$coln] = $lm->[$rown][$coln];
57             }
58             }
59 1         20 return __PACKAGE__->new( level => \@map );
60             }
61              
62             sub from_string {
63 5     5 1 552 my ( $self, $s ) = @_;
64 5         8 my @map;
65             my $cols;
66 5         67 for my $row ( split $/, $s ) {
67 23         75 push @map, [ split '', $row ];
68 23         43 my $newcols = $map[-1]->$#*;
69 23 100       40 if ( defined $cols ) {
70 18 100       39 if ( $cols != $newcols ) {
71 1         11 croak 'unequal column length at row index ' . $#map;
72             }
73             } else {
74 5         10 $cols = $newcols;
75             }
76             }
77 4         89 $self->level( \@map );
78 4         27 return $self;
79             }
80              
81             # TODO this might buffer and only print what differs across successive
82             # calls (for less bandwidth over an SSH connection)
83             sub to_panel {
84 8     8 1 3175 my $self = shift;
85 8         43 my ( $col, $row, $width, $height, $x, $y ) = map int, @_[ 0 .. 5 ];
86 8   100 5   42 my $oobfn = $_[6] // sub { return ' ' };
  5         11  
87 8         181 my $lm = $self->level;
88 8         51 my $map_cols = $lm->$#*;
89 8         14 my $map_rows = $lm->[0]->$#*;
90 8 100 100     57 croak "x must be within the level map" if $x < 0 or $x > $map_cols;
91 6 100 100     42 croak "y must be within the level map" if $y < 0 or $y > $map_rows;
92 4         12 my $scol = $x - int( $width / 2 );
93 4         8 my $srow = $y - int( $height / 2 );
94 4         7 my $s = '';
95              
96 4         10 for my $r ( $srow .. $srow + $height - 1 ) {
97 14         39 $s .= "\e[" . $row++ . ';' . $col . 'H';
98 14         20 for my $c ( $scol .. $scol + $width - 1 ) {
99 52 100 66     237 if ( $c < 0 or $c > $map_cols or $r < 0 or $r > $map_rows ) {
      100        
      66        
100 26         43 $s .= $oobfn->( $lm, $c, $r, $map_cols, $map_rows );
101             } else {
102 26         43 $s .= $lm->[$r][$c];
103             }
104             }
105             }
106 4         10 print $s;
107 4         11 return $self;
108             }
109              
110             sub to_string {
111 3     3 1 2436 my ($self) = @_;
112 3         70 my $lm = $self->level;
113 3         22 my $s = '';
114 3         8 for my $rowref ( $lm->@* ) { $s .= join( '', $rowref->@* ) . $/ }
  7         21  
115 3         15 return $s;
116             }
117              
118             sub to_terminal {
119 2     2 1 1977 my $self = shift;
120 2         11 my ( $col, $row ) = map int, @_;
121 2         51 my $lm = $self->level;
122 2         14 my $s = '';
123 2         6 for my $rowref ( $lm->@* ) {
124 6         19 $s .= "\e[" . $row++ . ';' . $col . 'H' . join( '', $rowref->@* );
125             }
126 2         6 print $s;
127 2         5 return $self;
128             }
129              
130             sub update_terminal {
131 1     1 1 330 my $self = $_[0];
132 1         7 my ( $col, $row ) = map int, @_[ 1 .. 2 ];
133 1         24 my $lm = $self->level;
134 1         7 my $s = '';
135 1         5 for my $point ( @_[ 3 .. $#_ ] ) {
136 2         10 $s .= "\e["
137             . ( $row + $point->[1] ) . ';'
138             . ( $col + $point->[0] ) . 'H'
139             . $lm->[ $point->[1] ][ $point->[0] ];
140             }
141 1         3 print $s;
142 1         2 return $self;
143             }
144              
145             1;
146             __END__