File Coverage

lib/Games/Checkers/SDL.pm
Criterion Covered Total %
statement 16 18 88.8
branch n/a
condition n/a
subroutine 6 6 100.0
pod n/a
total 22 24 91.6


line stmt bran cond sub pod time code
1             # Games::Checkers, Copyright (C) 1996-2012 Mikhael Goikhman, migo@cpan.org
2             #
3             # This program is free software: you can redistribute it and/or modify
4             # it under the terms of the GNU General Public License as published by
5             # the Free Software Foundation, either version 3 of the License, or
6             # (at your option) any later version.
7             #
8             # This program is distributed in the hope that it will be useful,
9             # but WITHOUT ANY WARRANTY; without even the implied warranty of
10             # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11             # GNU General Public License for more details.
12             #
13             # You should have received a copy of the GNU General Public License
14             # along with this program. If not, see .
15              
16 1     1   1117 use strict;
  1         2  
  1         37  
17 1     1   5 use warnings;
  1         2  
  1         45  
18              
19             package Games::Checkers::SDL;
20              
21 1     1   8 use Games::Checkers::Constants;
  1         3  
  1         8  
22 1     1   8 use Games::Checkers::Iterators;
  1         3  
  1         25  
23 1     1   8 use Games::Checkers::LocationConversions;
  1         2  
  1         109  
24              
25 1     1   503 use SDL;
  0            
  0            
26             use SDL::Event;
27             use SDL::Events;
28             use SDL::Rect;
29             use SDL::Surface;
30             use SDL::Video;
31             use SDL::Image;
32             use SDLx::Text;
33              
34             sub fill_rect_tiled ($$$) {
35             my $surface = shift || die;
36             my $rect = shift || die;
37             my $tile = shift || die;
38              
39             my ($x0, $y0, $w0, $h0) = ref($rect) eq 'ARRAY'
40             ? @$rect
41             : ($rect->x, $rect->y, $rect->w, $rect->h);
42             my $w = $tile->w;
43             my $h = $tile->h;
44              
45             for (my $x = $x0; $x < $x0 + $w0; $x += $w) {
46             for (my $y = $y0; $y < $y0 + $h0; $y += $h) {
47             SDL::Video::blit_surface($tile, 0, $surface, SDL::Rect->new($x, $y, $w, $h));
48             }
49             }
50             }
51              
52             sub new ($$$%) {
53             my $class = shift;
54             my $title = shift || die;
55             my $board = shift || die;
56             my %params = @_;
57              
58             my $image_dir = ($FindBin::Bin || "bin") . "/../data/images";
59             $image_dir = ($FindBin::Bin || "bin") . "/../share/pcheckers/images"
60             unless -d $image_dir;
61             die "No expected image dir $image_dir\n"
62             unless -d $image_dir && -x _;
63              
64             my $size = $board->get_size;
65             my $w = $size == 8 ? 800 : 1024;
66             my $h = $size == 8 ? 600 : 768;
67              
68             my $fullscreen = $params{fullscreen} ? 1 : 0;
69              
70             SDL::init(SDL_INIT_VIDEO);
71             my $mode = SDL_HWSURFACE | SDL_HWACCEL | ($fullscreen && SDL_FULLSCREEN);
72             my $display = SDL::Video::set_video_mode($w, $h, 32, $mode);
73              
74             SDL::Video::wm_set_caption("Checkers: $title", "Checkers");
75              
76             fill_rect_tiled($display, SDL::Rect->new(0, 0, $w, $h), SDL::Image::load("$image_dir/bg-tile.jpg"));
77              
78             SDL::Video::fill_rect($display, SDL::Rect->new(41, 41, 64 * $size + 6, 64 * $size + 6), 0x50d050);
79             SDL::Video::fill_rect($display, SDL::Rect->new(43, 43, 64 * $size + 2, 64 * $size + 2), 0x202020);
80             SDL::Video::fill_rect($display, SDL::Rect->new($w - 25, 4, 21, 21), 0xe0e0e0);
81             SDL::Video::fill_rect($display, SDL::Rect->new($w - 23, 6, 17, 17), 0x707070);
82              
83             my $title_text = SDLx::Text->new(
84             size => 24,
85             color => 0xffffdc,
86             bold => 1,
87             shadow => 1,
88             x => 44 + 64 * $size / 2,
89             y => 6,
90             h_align => 'center',
91             text => $title,
92             );
93             $title_text->write_to($display);
94              
95             my $coord_text = SDLx::Text->new(
96             size => 20,
97             color => 0xd8d8d0,
98             shadow => 1,
99             h_align => 'center',
100             );
101             $coord_text->write_xy($display, 28, 66 + 64 * ($size - $_), $_) for 1 .. $size;
102             $coord_text->write_xy($display, 77 + 64 * $_, $h - 40, chr(ord('a') + $_)) for 0 .. $size-1;
103              
104             my $bg_surface = SDL::Surface->new(0, 64 * $size, 64 * $size);
105              
106             my $self = {
107             board => $board,
108             cells => [
109             SDL::Image::load("$image_dir/cell-white.png"),
110             SDL::Image::load("$image_dir/cell-black.png"),
111             ],
112             pieces => {
113             &Pawn => {
114             &White => SDL::Image::load("$image_dir/pawn-white.png"),
115             &Black => SDL::Image::load("$image_dir/pawn-black.png"),
116             },
117             &King => {
118             &White => SDL::Image::load("$image_dir/king-white.png"),
119             &Black => SDL::Image::load("$image_dir/king-black.png"),
120             },
121             },
122             w => $w,
123             h => $h,
124             move_str_y => 0,
125             display => $display,
126             bg_surface => $bg_surface,
127             event => SDL::Event->new,
128             text => SDLx::Text->new(shadow => 1, shadow_offset => 2, size => 20),
129             mouse_pressed => 0,
130             fullscreen => $fullscreen,
131             };
132              
133             bless $self, $class;
134              
135             return $self;
136             }
137              
138             sub init ($) {
139             my $self = shift;
140              
141             my $size = $self->{board}->get_size;
142              
143             for my $x (0 .. $size - 1) {
144             for my $y (0 .. $size - 1) {
145             SDL::Video::blit_surface(
146             $self->{cells}[($x + $y) % 2],
147             0,
148             $self->{bg_surface},
149             SDL::Rect->new(64 * $x, 64 * $y, 64, 64)
150             );
151             }
152             }
153              
154             return $self;
155             }
156              
157             sub quit ($) {
158             exit(0);
159             }
160              
161             sub pause ($) {
162             my $self = shift;
163              
164             my $size = $self->{board}->get_size;
165             my $display = $self->{display};
166              
167             my $display_copy = SDL::Video::display_format($display);
168              
169             $self->{paused} = 1;
170             SDLx::Text->new(
171             size => 110,
172             color => 0xffffff,
173             bold => 0,
174             shadow => 1,
175             x => $self->{w} / 2,
176             y => $self->{h} / 2 - 58,
177             h_align => 'center',
178             text => 'PAUSED',
179             )->write_to($display);
180              
181             while ($self->process_pending_events != 2) {
182             select(undef, undef, undef, 0.1);
183             }
184              
185             $self->{paused} = 0;
186             SDL::Video::blit_surface($display_copy, 0, $display, 0);
187             }
188              
189             sub toggle_fullscreen ($) {
190             my $self = shift;
191              
192             $self->{fullscreen} ^= 1;
193             SDL::Video::wm_toggle_fullscreen($self->{display});
194             }
195              
196             sub process_pending_events ($) {
197             my $self = shift;
198              
199             SDL::Video::update_rect($self->{display}, 0, 0, 0, 0);
200              
201             my $event = $self->{event};
202              
203             SDL::Events::pump_events();
204             while (SDL::Events::poll_event($event)) {
205             $self->toggle_fullscreen, next
206             if $event->type == SDL_KEYDOWN && $event->key_sym == SDLK_RETURN
207             && $event->key_mod & KMOD_ALT
208             || $event->type == SDL_KEYDOWN && $event->key_sym == SDLK_F11
209             || $event->type == SDL_KEYDOWN && $event->key_sym == SDLK_f
210             || $event->type == SDL_MOUSEBUTTONDOWN
211             && abs($event->motion_x - $self->{w} + 14) <= 10
212             && abs($event->motion_y - 14) <= 10;
213              
214             return 2
215             if $self->{paused}
216             && ($event->type == SDL_KEYDOWN || $event->type == SDL_MOUSEBUTTONDOWN);
217              
218             $self->{mouse_pressed} = $event->type == SDL_MOUSEBUTTONDOWN
219             if $event->button_button == SDL_BUTTON_LEFT;
220              
221             $self->quit
222             if $event->type == SDL_QUIT
223             || $event->type == SDL_KEYDOWN && $event->key_sym == SDLK_ESCAPE
224             || $event->type == SDL_KEYDOWN && $event->key_sym == SDLK_q;
225              
226             $self->pause
227             if $event->type == SDL_KEYDOWN && $event->key_sym == SDLK_p
228             || $event->type == SDL_KEYDOWN && $event->key_sym == SDLK_SPACE;
229             }
230              
231             return 1;
232             }
233              
234             sub sleep ($$) {
235             my $self = shift;
236             my $fsecs = (shift || 0) * 50;
237              
238             do {
239             $self->process_pending_events;
240             select(undef, undef, undef, 0.02) if $fsecs--;
241             } while $fsecs >= 0;
242             }
243              
244             sub show_board ($) {
245             my $self = shift;
246              
247             my $board = $self->{board};
248             my $size = $board->get_size;
249              
250             # draw empty board first
251             if (0) {
252             SDL::Video::blit_surface(
253             $self->{bg_surface},
254             0,
255             $self->{display},
256             SDL::Rect->new(44, 44, 64 * $size, 64 * $size),
257             );
258             } else {
259             for my $x (0 .. $size - 1) {
260             for my $y (0 .. $size - 1) {
261             SDL::Video::blit_surface(
262             $self->{cells}[($x + $y) % 2],
263             0,
264             $self->{display},
265             SDL::Rect->new(44 + 64 * $x, 44 + 64 * $y, 64, 64)
266             );
267             }
268             }
269             }
270              
271             for my $color (White, Black) {
272             my $iterator = Games::Checkers::FigureIterator->new($board, $color);
273             for my $location ($iterator->all) {
274             my $piece = $board->piece($location);
275             my ($x, $y) = location_to_arr($location);
276             SDL::Video::blit_surface(
277             $self->{pieces}{$piece}{$color},
278             0,
279             $self->{display},
280             SDL::Rect->new(52 + 64 * ($x - 1), 52 + 64 * ($size - $y), 48, 48)
281             );
282             }
283             }
284              
285             $self->process_pending_events;
286             }
287              
288             sub show_move ($$$$$) {
289             my $self = shift;
290             my $move = shift;
291             my $color = shift;
292             my $count = shift;
293              
294             my $str = $move->dump;
295             my $x = 0;
296             if ($count % 2 == 0) {
297             $self->{move_msg_y} += 20;
298             $str = ($count / 2 + 1) . ". $str";
299             } else {
300             $x = 117;
301             }
302              
303             $self->{text}->write_xy($self->{display}, 580 + $x, $self->{move_msg_y}, $str);
304              
305             $self->process_pending_events;
306             }
307              
308             sub show_result ($$) {
309             my $self = shift;
310             my $message = shift;
311              
312             my $text = $self->{text};
313             $text->h_align('center');
314             $text->color([220, 220, 150]);
315             $text->write_xy($self->{display}, ($self->{w} + 540) / 2, 0, $message);
316             $self->sleep(6);
317             }
318              
319             1;