File Coverage

blib/lib/SDLx/Sprite/Animated.pm
Criterion Covered Total %
statement 181 192 94.2
branch 54 70 77.1
condition 5 12 41.6
subroutine 32 34 94.1
pod 0 18 0.0
total 272 326 83.4


line stmt bran cond sub pod time code
1             package SDLx::Sprite::Animated;
2 2     2   1213 use strict;
  2         3  
  2         53  
3 2     2   12 use warnings;
  2         3  
  2         47  
4              
5 2     2   9 use Scalar::Util 'refaddr';
  2         3  
  2         79  
6 2     2   7 use SDL;
  2         2  
  2         91  
7 2     2   8 use SDL::Video;
  2         2  
  2         232  
8 2     2   247 use SDL::Rect;
  2         2  
  2         530  
9 2     2   276 use SDLx::Sprite;
  2         5  
  2         37  
10 2     2   8 use SDLx::Validate;
  2         2  
  2         44  
11              
12 2     2   7 use base 'SDLx::Sprite';
  2         2  
  2         2734  
13              
14             # inside out
15             my %_ticks;
16             my %_width;
17             my %_height;
18             my %_step_x;
19             my %_step_y;
20             my %_offset_x;
21             my %_offset_y;
22             my %_type;
23             my %_max_loops;
24             my %_ticks_per_frame;
25             my %_current_frame;
26             my %_current_loop;
27             my %_sequences;
28             my %_sequence;
29             my %_started;
30             my %_direction;
31              
32             sub new {
33 10     10 0 8113 my ( $class, %options ) = @_;
34              
35 10         19 my ( $w, $h );
36 10 100 33     53 if ( exists $options{clip} ) {
    100          
    50          
37 2         11 ( $w, $h ) = ( $options{clip}->w, $options{clip}->h );
38             } elsif ( exists $options{rect} ) {
39 7         42 ( $w, $h ) = ( $options{rect}->w, $options{rect}->h );
40             } elsif ( exists $options{width} && $options{height} ) {
41 1         3 ( $w, $h ) = ( $options{width}, $options{height} );
42             }
43              
44 10         68 my $self = $class->SUPER::new(%options);
45              
46 10         29 $self->_store_geometry( $w, $h );
47              
48 10 100       29 $self->step_x( exists $options{step_x} ? $options{step_x} : $self->clip->w );
49 10 100       31 $self->step_y( exists $options{step_y} ? $options{step_y} : $self->clip->h );
50 10 100       37 $_offset_x{ refaddr $self} = exists $options{clip} ? $options{clip}->x : 0;
51 10 100       29 $_offset_y{ refaddr $self} = exists $options{clip} ? $options{clip}->y : 0;
52              
53 10 100       37 $self->max_loops( exists $options{max_loops} ? $options{max_loops} : 0 );
54 10 50       36 $self->ticks_per_frame( exists $options{ticks_per_frame} ? $options{ticks_per_frame} : 1 );
55 10 100       32 $self->type( exists $options{type} ? $options{type} : 'circular' );
56              
57 10 100       25 if ( exists $options{sequences} ) {
58 1         5 $_sequences{ refaddr $self} = $options{sequences};
59             } else {
60 9         23 $self->_init_default_sequence();
61             }
62 10 100       24 $self->sequence( $options{sequence} ) if exists $options{sequence};
63              
64 10         26 $_ticks{ refaddr $self} = 0;
65 10         16 $_direction{ refaddr $self} = 1;
66              
67 10         31 return $self;
68             }
69              
70             sub DESTROY {
71 10     10   1037 my $self = shift;
72 10         29 delete $_ticks{ refaddr $self};
73 10         178 delete $_width{ refaddr $self};
74 10         18 delete $_height{ refaddr $self};
75 10         20 delete $_step_x{ refaddr $self};
76 10         21 delete $_step_y{ refaddr $self};
77 10         21 delete $_offset_x{ refaddr $self};
78 10         22 delete $_offset_y{ refaddr $self};
79 10         20 delete $_type{ refaddr $self};
80 10         17 delete $_max_loops{ refaddr $self};
81 10         15 delete $_ticks_per_frame{ refaddr $self};
82 10         16 delete $_current_frame{ refaddr $self};
83 10         19 delete $_current_loop{ refaddr $self};
84 10         42 delete $_sequences{ refaddr $self};
85 10         15 delete $_sequence{ refaddr $self};
86 10         22 delete $_started{ refaddr $self};
87 10         60 delete $_direction{ refaddr $self};
88             }
89              
90             sub load {
91 1     1 0 652 my $self = shift;
92 1         2 my $image = shift;
93 1         7 $self->SUPER::load($image);
94 1         2 $self->_restore_geometry;
95 1         2 $self->_init_default_sequence;
96 1         4 return $self;
97             }
98              
99             sub _init_default_sequence {
100 10     10   13 my $self = shift;
101              
102 10         22 my $max_x = int( ( $self->surface->w - $_offset_x{ refaddr $self} ) / $self->step_x );
103 10         23 my $max_y = int( ( $self->surface->h - $_offset_y{ refaddr $self} ) / $self->step_y );
104              
105 10         16 my @sequence;
106 10         19 foreach my $y ( 0 .. $max_y - 1 ) {
107 27         26 foreach my $x ( 0 .. $max_x - 1 ) {
108 129         176 push @sequence, [ $x, $y ];
109             }
110             }
111 10         48 $_sequences{ refaddr $self} = { 'default' => \@sequence };
112 10         28 $self->sequence('default');
113             }
114              
115             sub _store_geometry {
116 10     10   12 my ( $self, $w, $h ) = @_;
117              
118 10         37 $_width{ refaddr $self} = $w;
119 10         26 $_height{ refaddr $self} = $h;
120              
121 10         27 $self->_restore_geometry;
122             }
123              
124             sub _restore_geometry {
125 13     13   15 my $self = shift;
126              
127 13 50       57 $self->clip->w( $_width{ refaddr $self} ) if exists $_width{ refaddr $self};
128 13 50       46 $self->clip->h( $_height{ refaddr $self} ) if exists $_height{ refaddr $self};
129 13 50       47 $self->rect->w( $_width{ refaddr $self} ) if exists $_width{ refaddr $self};
130 13 50       53 $self->rect->h( $_height{ refaddr $self} ) if exists $_height{ refaddr $self};
131             }
132              
133             sub step_y {
134 20     20 0 21 my ( $self, $step_y ) = @_;
135              
136 20 100       33 if ($step_y) {
137 10         18 $_step_y{ refaddr $self} = $step_y;
138             }
139              
140 20         34 return $_step_y{ refaddr $self};
141             }
142              
143             sub step_x {
144 20     20 0 21 my ( $self, $step_x ) = @_;
145              
146 20 100       33 if ($step_x) {
147 10         22 $_step_x{ refaddr $self} = $step_x;
148             }
149              
150 20         49 return $_step_x{ refaddr $self};
151             }
152              
153             sub type {
154 10     10 0 12 my ( $self, $type ) = @_;
155              
156 10 50       19 if ($type) {
157 10         27 $_type{ refaddr $self} = lc $type;
158             }
159              
160 10         26 return $_type{ refaddr $self};
161             }
162              
163             sub max_loops {
164 10     10 0 13 my $self = shift;
165              
166 10 50       20 if (@_) {
167 10         22 $_max_loops{ refaddr $self} = shift;
168             }
169              
170 10         18 return $_max_loops{ refaddr $self};
171             }
172              
173             sub ticks_per_frame {
174 10     10 0 12 my ( $self, $ticks ) = @_;
175              
176 10 50       19 if ($ticks) {
177 10         24 $_ticks_per_frame{ refaddr $self} = $ticks;
178             }
179              
180 10         21 return $_ticks_per_frame{ refaddr $self};
181             }
182              
183             sub current_frame {
184 22     22 0 385 my ( $self, $frame ) = @_;
185 22         94 return $_current_frame{ refaddr $self};
186             }
187              
188             sub current_loop {
189 12     12 0 491 my ($self) = @_;
190 12         55 return $_current_loop{ refaddr $self };
191             }
192              
193             sub set_sequences {
194 6     6 0 18 my ( $self, %sequences ) = @_;
195              
196             # TODO: Validate sequences.
197 6         20 $_sequences{ refaddr $self} = \%sequences;
198              
199 6         22 return $self;
200             }
201              
202             sub sequence {
203 18     18 0 1817 my ( $self, $sequence ) = @_;
204 18         33 my $me = refaddr $self;
205              
206 18 50       34 if ($sequence) {
207              
208 18 50       48 if ( !defined( $_sequences{ $me }{$sequence} ) ) {
209 0         0 warn 'Unknown sequence: ', $sequence;
210 0         0 return;
211             }
212 18         25 $_sequence{ $me } = $sequence;
213 18         19 $_current_frame{ $me } = 1;
214 18         21 $_current_loop{ $me } = 1;
215 18         23 $_direction{ $me } = 1;
216 18         32 $self->_update_clip;
217             }
218              
219 18         31 return $_sequence{ $me };
220             }
221              
222             sub _sequence {
223 181     181   159 my $self = shift;
224 181         570 return $_sequences{ refaddr $self}{ $_sequence{ refaddr $self} };
225             }
226              
227             sub _frame {
228 72     72   52 my $self = shift;
229 72         81 return $self->_sequence->[ $_current_frame{ refaddr $self} - 1 ];
230             }
231              
232             sub next {
233 43     43 0 24469 my $self = shift;
234 43         96 my $me = refaddr $self;
235              
236 43 50       35 return if @{ $self->_sequence } == 1;
  43         58  
237              
238 43 100 100     110 return if $_max_loops{ $me } && $_current_loop{ $me } > $_max_loops{ $me };
239              
240 42         66 my $next_frame = ( $_current_frame{ $me } - 1 + $_direction{ $me } ) % @{ $self->_sequence };
  42         54  
241              
242 42 100       72 if ( $next_frame == 0 ) {
243 10 100       34 $_current_loop{ $me }++ if $_type{ $me } eq 'circular';
244              
245 10 100       33 if ( $_type{ $me } eq 'reverse' ) {
246              
247 3 100       9 if ( $_direction{ $me } == 1 ) {
248 2         2 $next_frame = @{ $self->_sequence } - 2;
  2         5  
249             } else {
250 1         3 $_current_loop{ $me }++;
251             }
252              
253 3         5 $_direction{ $me } *= -1;
254             }
255             }
256 42         49 $_current_frame{ $me } = $next_frame + 1;
257              
258 42         71 $self->_update_clip;
259              
260 42         64 return $self;
261             }
262              
263             sub previous {
264 11     11 0 1482 my $self = shift;
265              
266 11 50 33     41 return if $_max_loops{ refaddr $self} && $_current_loop{ refaddr $self } > $_max_loops{ refaddr $self};
267              
268 11         20 $_ticks{ refaddr $self} = 0;
269              
270 11 50       8 return if @{ $self->_sequence } == 1;
  11         15  
271              
272 11         26 my $previous_frame = ( $_current_frame{ refaddr $self} - 1 - $_direction{ refaddr $self} ) % @{ $self->_sequence };
  11         14  
273              
274 11 100       19 if ( $previous_frame == 0 ) {
275 5 100       17 if ( $_type{ refaddr $self} eq 'reverse' ) {
276              
277 3 100       9 if ( $_direction{ refaddr $self} == -1 ) {
278 1         2 $previous_frame = 1;
279             }
280              
281 3         6 $_direction{ refaddr $self} *= -1;
282             }
283             }
284 11         156 $_current_frame{ refaddr $self} = $previous_frame + 1;
285              
286 11         15 $self->_update_clip;
287              
288 11         14 return $self;
289             }
290              
291             sub reset {
292 1     1 0 2 my $self = shift;
293              
294 1         4 $self->stop;
295 1         4 $_current_frame{ refaddr $self} = 1;
296 1         3 $self->_update_clip;
297              
298 1         4 return $self;
299             }
300              
301             sub start {
302 0     0 0 0 my $self = shift;
303              
304 0         0 $_started{ refaddr $self} = 1;
305              
306 0         0 return $self;
307             }
308              
309             sub stop {
310 1     1 0 1 my $self = shift;
311              
312 1         5 $_started{ refaddr $self} = 0;
313              
314 1         1 return $self;
315             }
316              
317             sub _update_clip {
318 72     72   57 my $self = shift;
319              
320 72         153 my $clip = $self->clip;
321 72         102 my $frame = $self->_frame;
322              
323 72         276 $clip->x( $_offset_x{ refaddr $self} + $frame->[0] * $_step_x{ refaddr $self} );
324 72         240 $clip->y( $_offset_y{ refaddr $self} + $frame->[1] * $_step_y{ refaddr $self} );
325             }
326              
327             sub alpha_key {
328 2     2 0 7 my $self = shift;
329 2         9 $self->SUPER::alpha_key(@_);
330 2         4 $self->_restore_geometry;
331 2         4 return $self;
332             }
333              
334             sub draw {
335 0     0 0   my ( $self, $surface ) = @_;
336              
337 0           $surface = SDLx::Validate::surface($surface);
338              
339 0           $_ticks{ refaddr $self}++;
340 0 0 0       $self->next if $_started{ refaddr $self} && $_ticks{ refaddr $self} % $_ticks_per_frame{ refaddr $self} == 0;
341              
342 0           SDL::Video::blit_surface( $self->surface, $self->clip, $surface, $self->rect );
343              
344 0           return $self;
345             }
346              
347             1;
348