File Coverage

lib/Games/Checkers/SDL.pm
Criterion Covered Total %
statement 19 21 90.4
branch n/a
condition n/a
subroutine 7 7 100.0
pod n/a
total 26 28 92.8


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   1137 use strict;
  1         2  
  1         49  
17 1     1   5 use warnings;
  1         2  
  1         32  
18              
19             package Games::Checkers::SDL;
20              
21 1     1   5 use Games::Checkers::Constants;
  1         1  
  1         5  
22 1     1   5 use Games::Checkers::Iterators;
  1         1  
  1         19  
23 1     1   4 use Games::Checkers::Board;
  1         2  
  1         29  
24 1     1   4 use Games::Checkers::Rules;
  1         1  
  1         15  
25              
26 1     1   271 use SDL;
  0            
  0            
27             use SDL::Event;
28             use SDL::Events;
29             use SDL::Rect;
30             use SDL::Surface;
31             use SDL::Video;
32             use SDL::Image;
33             use SDLx::Text;
34             use SDL::GFX::Primitives;
35              
36             sub rect_to_xywh ($;$) {
37             my $surface = shift || die;
38             my $rect = shift;
39              
40             return (0, 0, $surface->w, $surface->h)
41             unless $rect;
42              
43             $rect = [ $rect->x, $rect->y, $rect->w, $rect->h ]
44             unless ref($rect) eq 'ARRAY';
45              
46             $rect->[2] ||= $surface->w - $rect->[0];
47             $rect->[3] ||= $surface->h - $rect->[1];
48              
49             return @$rect;
50             }
51              
52             sub fill_rect_tiled ($$$) {
53             my $surface = shift || die;
54             my $rect = shift;
55             my $tile = shift || die;
56              
57             my ($x0, $y0, $w0, $h0) = rect_to_xywh($surface, $rect);
58             my $w = $tile->w;
59             my $h = $tile->h;
60              
61             for (my $x = $x0; $x < $x0 + $w0; $x += $w) {
62             for (my $y = $y0; $y < $y0 + $h0; $y += $h) {
63             SDL::Video::blit_surface($tile, 0, $surface, SDL::Rect->new($x, $y, $w, $h));
64             }
65             }
66             }
67              
68             sub new ($$$%) {
69             my $class = shift;
70             my $title = shift || die;
71             my $board = shift || die;
72             my %params = @_;
73              
74             my $image_dir = ($FindBin::Bin || "bin") . "/../data/images";
75             $image_dir = ($FindBin::Bin || "bin") . "/../share/pcheckers/images"
76             unless -d $image_dir;
77             die "No expected image dir $image_dir\n"
78             unless -d $image_dir && -x _;
79              
80             my $fullscreen = $params{fullscreen} ? 1 : 0;
81              
82             my $self = {
83             board => $board,
84             title => $title,
85             pieces => {
86             &Pawn => {
87             &White => SDL::Image::load("$image_dir/pawn-white.png"),
88             &Black => SDL::Image::load("$image_dir/pawn-black.png"),
89             },
90             &King => {
91             &White => SDL::Image::load("$image_dir//king-white.png"),
92             &Black => SDL::Image::load("$image_dir/king-black.png"),
93             },
94             },
95             ply_strs => [],
96             ply_o => 0,
97             ply_l => 0,
98             event => SDL::Event->new,
99             text => SDLx::Text->new(shadow => 1, shadow_offset => 2, size => 20),
100             mouse_pressed => 0,
101             skip_unpress => 0,
102             fullscreen => $fullscreen,
103             image_dir => $image_dir,
104             };
105              
106             bless $self, $class;
107              
108             $self->init_video;
109              
110             SDL::Video::wm_set_caption("Checkers: $title", "Checkers");
111              
112             return $self;
113             }
114              
115             sub init_video ($) {
116             my $self = shift;
117              
118             my $board = $self->{board};
119              
120             my $size_x = $board->size_x;
121             my $size_y = $board->size_y;
122              
123             my ($w, $h, $title_height, $helper_x) =
124             $size_x <= 6 ? ( 640, 480, 21, 428) :
125             $size_x <= 8 ? ( 800, 600, 24, 576) :
126             $size_x <= 10 ? (1024, 768, 26, 728) :
127             $size_x <= 14 ? (1280, 1024, 28, 940) :
128             die "Sorry, the board size ${size_x}x$size_y is not supported\n";
129              
130             my $b_w = 64 * $size_x;
131             my $b_h = 64 * $size_y;
132             my $b_x = ($helper_x - $b_w) / 2 + 10;
133             my $b_y = ($h - $b_h) / 2;
134             my $helper_mid_x = ($w + $helper_x) / 2;
135              
136             SDL::init(SDL_INIT_VIDEO);
137             my $mode = SDL_HWSURFACE | SDL_HWACCEL | ($self->{fullscreen} && SDL_FULLSCREEN);
138             my $display = SDL::Video::set_video_mode($w, $h, 32, $mode);
139              
140             my $bg = SDL::Surface->new(SDL_HWSURFACE | SDL_PHYSPAL, $w, $h);
141              
142             my $image_dir = $self->{image_dir};
143             fill_rect_tiled($bg, 0, SDL::Image::load("$image_dir/bg-tile.jpg"));
144              
145             SDL::Video::fill_rect($bg, SDL::Rect->new($b_x - 3, $b_y - 3, $b_w + 6, $b_h + 6), 0x50d050ff);
146             SDL::Video::fill_rect($bg, SDL::Rect->new($b_x - 1, $b_y - 1, $b_w + 2, $b_h + 2), 0x202020ff);
147             SDL::Video::fill_rect($bg, SDL::Rect->new($w - 18, 2, 16, 16), 0xe0e0e0);
148             SDL::Video::fill_rect($bg, SDL::Rect->new($w - 16, 4, 12, 12), 0x707070);
149             SDL::Video::fill_rect($bg, SDL::Rect->new($w - 34, 6, 8, 8), 0xf0f0f0);
150             SDL::Video::fill_rect($bg, SDL::Rect->new($w - 58, 2, 16, 16), 0xc0c0c0);
151             SDL::Video::fill_rect($bg, SDL::Rect->new($w - 55, 5, 10, 10), 0xa0a0a0);
152              
153             my $coord_text = SDLx::Text->new(
154             size => 20,
155             color => 0xd8d8d0,
156             shadow => 1,
157             h_align => 'center',
158             );
159             $coord_text->write_xy($bg, $b_x - 16, $b_y + 22 + 64 * ($size_y - $_), $_) for 1 .. $size_y;
160             $coord_text->write_xy($bg, $b_x - 31 + 64 * $_, $b_y + $b_h + 4, $board->ind_to_chr($_)) for 1 .. $size_x;
161              
162             SDL::Video::blit_surface($bg, 0, $display, 0);
163              
164             my @cells = (
165             SDL::Image::load("$image_dir/cell-white.png"),
166             SDL::Image::load("$image_dir/cell-black.png"),
167             );
168              
169             for my $x (0 .. $size_x - 1) {
170             for my $y (0 .. $size_y - 1) {
171             SDL::Video::blit_surface(
172             $cells[($x + $y + 1 + $::RULES{BOTTOM_LEFT_CELL}) % 2],
173             0,
174             $bg,
175             SDL::Rect->new($b_x + 64 * $x, $b_y + 64 * $y, 64, 64)
176             );
177             }
178             }
179              
180             %$self = (
181             %$self,
182             w => $w,
183             h => $h,
184             size_x => $size_x,
185             size_y => $size_y,
186             b_x => $b_x,
187             b_y => $b_y,
188             b_w => $b_w,
189             b_h => $b_h,
190             title_height => $title_height,
191             helper_x => $helper_x,
192             helper_mid_x => $helper_mid_x,
193             display => $display,
194             bg => $bg,
195             ply_m => int($h / 20 - 1) * 2,
196             );
197              
198             return $self;
199             }
200              
201             sub init ($) {
202             my $self = shift;
203              
204             $self->show_title;
205              
206             return $self;
207             }
208              
209             sub blit_bg ($;$) {
210             my $self = shift;
211             my $rect = shift;
212              
213             my $display = $self->{display};
214              
215             my ($x, $y, $w, $h) = rect_to_xywh($display, $rect);
216             $rect = SDL::Rect->new($x, $y, $w, $h);
217             SDL::Video::blit_surface($self->{bg}, $rect, $display, $rect);
218             }
219              
220             sub show_title ($;$) {
221             my $self = shift;
222             my $title = shift || $self->{title};
223              
224             my $display = $self->{display};
225              
226             $self->blit_bg([ 0, 0, $self->{helper_x}, $self->{b_y} - 4 ]);
227              
228             my $title_text = SDLx::Text->new(
229             size => $self->{title_height},
230             color => 0xffffdc,
231             bold => 1,
232             shadow => 1,
233             x => $self->{helper_x} / 2,
234             y => 6,
235             h_align => 'center',
236             text => $title,
237             );
238             $title_text->write_to($display);
239             }
240              
241             sub clear_helper ($) {
242             my $self = shift;
243              
244             $self->blit_bg([$self->{helper_x}, 20]);
245             }
246              
247             sub restart ($$) {
248             my $self = shift;
249             my $board = shift;
250              
251             $self->{ply_strs} = [];
252             $self->{ply_o} = 0;
253             $self->{ply_l} = 0;
254             $self->{board} = $board;
255              
256             $self->clear_helper;
257             }
258              
259             sub quit ($) {
260             return 1;
261             }
262              
263             sub pause ($) {
264             my $self = shift;
265              
266             my $display = $self->{display};
267             my $display_copy = SDL::Video::display_format($display);
268              
269             $self->{paused} = 1;
270             SDLx::Text->new(
271             size => 110,
272             color => 0xffffff,
273             bold => 0,
274             shadow => 1,
275             x => $self->{w} / 2,
276             y => $self->{h} / 2 - 58,
277             h_align => 'center',
278             text => 'PAUSED',
279             )->write_to($display);
280              
281             while ($self->process_pending_events != 1) {
282             select(undef, undef, undef, 0.1);
283             }
284              
285             $self->{paused} = 0;
286             SDL::Video::blit_surface($display_copy, 0, $display, 0);
287             }
288              
289             sub toggle_fullscreen ($) {
290             my $self = shift;
291              
292             $self->{fullscreen} ^= 1;
293             SDL::Video::wm_toggle_fullscreen($self->{display});
294             }
295              
296             sub update_display ($) {
297             my $self = shift;
298              
299             SDL::Video::update_rect($self->{display}, 0, 0, 0, 0);
300              
301             return 1;
302             }
303              
304             sub process_pending_events ($;$) {
305             my $self = shift;
306             my $want_unpress = shift;
307              
308             $self->update_display;
309              
310             my $event = $self->{event};
311              
312             SDL::Events::pump_events();
313             while (SDL::Events::poll_event($event)) {
314             $self->{skip_unpress} = 0, next
315             if $self->{skip_unpress} == SDL_KEYDOWN && $event->type == SDL_KEYUP
316             || $self->{skip_unpress} == SDL_MOUSEBUTTONDOWN && $event->type == SDL_MOUSEBUTTONUP;
317              
318             my $pressed_button = $event->type == SDL_MOUSEBUTTONDOWN
319             && $event->motion_y < 20 && $event->motion_x >= $self->{w} - 20 * 3
320             ? 1 + int(($self->{w} - $event->motion_x) / 20) : 0;
321              
322             $self->toggle_fullscreen, $self->{skip_unpress} = $event->type, next
323             if $event->type == SDL_KEYDOWN && $event->key_sym == SDLK_RETURN
324             && $event->key_mod & KMOD_ALT
325             || $event->type == SDL_KEYDOWN && $event->key_sym == SDLK_F11
326             || $event->type == SDL_KEYDOWN && $event->key_sym == SDLK_f
327             || $pressed_button == 1;
328              
329             return 1
330             if ($self->{paused} || $want_unpress)
331             && ($event->type == SDL_KEYUP || $event->type == SDL_MOUSEBUTTONUP);
332              
333             next
334             if $self->{paused};
335              
336             return -1
337             if $event->type == SDL_KEYDOWN && $event->key_sym == SDLK_r
338             || $pressed_button == 3;
339              
340             $self->{mouse_pressed} = $event->type == SDL_MOUSEBUTTONDOWN
341             if $event->button_button == SDL_BUTTON_LEFT;
342              
343             return -2
344             if $event->type == SDL_QUIT
345             || $event->type == SDL_KEYDOWN && $event->key_sym == SDLK_ESCAPE
346             || $event->type == SDL_KEYDOWN && $event->key_sym == SDLK_q;
347              
348             $self->{skip_unpress} = $event->type, return $self->pause
349             if $event->type == SDL_KEYDOWN && $event->key_sym == SDLK_p
350             || $event->type == SDL_KEYDOWN && $event->key_sym == SDLK_SPACE
351             || $pressed_button == 2;
352             }
353              
354             return 0;
355             }
356              
357             sub sleep ($$) {
358             my $self = shift;
359             my $fsecs = (shift || 0) * 50;
360              
361             do {
362             my $rv = $self->process_pending_events;
363             return $rv if $rv < 0;
364             select(undef, undef, undef, 0.02) if $fsecs--;
365             } while $fsecs >= 0;
366              
367             return 0;
368             }
369              
370             sub wait ($;$) {
371             my $self = shift;
372              
373             while ($self->update_display && SDL::Events::wait_event) {
374             my $rv = $self->process_pending_events("want_unpress");
375             return $rv if $rv < 0;
376             last if $rv == 1;
377             }
378              
379             return 0;
380             }
381              
382             use constant QUIT_PRESSED => -2;
383             use constant RESTART_PRESSED => -1;
384             use constant MISC_PRESSED => 0;
385             use constant RECT_PRESSED => 1;
386             use constant BOARD_LOC_PRESSED => 2;
387             use constant KEY_PRESSED => 3;
388              
389             sub wait_for_press ($;$) {
390             my $self = shift;
391             my $rects = shift || [];
392              
393             my $rv = $self->wait;
394             return $rv if $rv < 0;
395              
396             my $event = $self->{event};
397              
398             if ($event->type == SDL_KEYUP) {
399             return (KEY_PRESSED, $event->key_sym);
400             }
401              
402             die "Internal bug..." unless $event->type == SDL_MOUSEBUTTONUP;
403              
404             my $mouse_x = $event->motion_x;
405             my $mouse_y = $event->motion_y;
406              
407             # check rectangles
408             for my $i (0 .. @$rects - 1) {
409             my ($x, $y, $w, $h) = rect_to_xywh($self->{display}, $rects->[$i]);
410             return (RECT_PRESSED, $i)
411             if $mouse_x >= $x && $mouse_x < $x + $w
412             && $mouse_y >= $y && $mouse_y < $y + $h;
413             }
414              
415             # check board locations
416             $mouse_x -= $self->{b_x};
417             $mouse_y -= $self->{b_y};
418             if (
419             $mouse_x >= 0 && $mouse_x < $self->{b_w} &&
420             $mouse_y >= 0 && $mouse_y < $self->{b_h}
421             ) {
422             my $x = 1 + int($mouse_x / 64);
423             my $y = $self->{size_y} - int($mouse_y / 64);
424             return (BOARD_LOC_PRESSED, $self->{board}->arr_to_loc($x, $y), $event->button_button == SDL_BUTTON_RIGHT)
425             if ($x + $y + $::RULES{BOTTOM_LEFT_CELL}) % 2;
426             }
427              
428             return MISC_PRESSED;
429             }
430              
431             sub hold ($) {
432             my $self = shift;
433              
434             my $rv = $self->wait;
435              
436             return $rv == QUIT_PRESSED ? QUIT_PRESSED : MISC_PRESSED;
437             }
438              
439             sub dim_display_rect ($$;$) {
440             my $self = shift;
441             my $rect = shift;
442             my $alpha = shift || 128;
443              
444             my $display = $self->{display};
445              
446             my ($x, $y, $w, $h) = rect_to_xywh($display, $rect);
447             my $dim_surface = SDL::Surface->new(SDL_ASYNCBLIT | SDL_HWSURFACE, $w, $h, 8);
448             SDL::Video::blit_surface($dim_surface, 0, $display, SDL::Rect->new($x, $y, $w, $h))
449             if SDL::Video::set_alpha($dim_surface, SDL_SRCALPHA, $alpha) == 0;
450             }
451              
452             sub show_board ($;$) {
453             my $self = shift;
454             my $dim = shift || 0;
455              
456             my $display = $self->{display};
457             my $board = $self->{board};
458             my $size_y = $self->{size_y};
459              
460             # draw empty board first
461             $self->blit_bg([ $self->{b_x}, $self->{b_y}, $self->{b_w}, $self->{b_h} ]);
462              
463             for my $color (White, Black) {
464             my $iterator = Games::Checkers::FigureIterator->new($board, $color);
465             for my $loc ($iterator->all) {
466             my $piece = $board->piece($loc);
467             my ($x, $y) = $board->loc_to_arr($loc);
468             SDL::Video::blit_surface(
469             $self->{pieces}{$piece}{$color}, 0,
470             $display, SDL::Rect->new(8 + $self->{b_x} + 64 * ($x - 1), 8 + $self->{b_y} + 64 * ($size_y - $y), 48, 48)
471             );
472             }
473             }
474              
475             $self->dim_display_rect([ $self->{b_x}, $self->{b_y}, $self->{b_w}, $self->{b_h} ], 60) if $dim;
476              
477             $self->process_pending_events;
478             }
479              
480             sub show_last_move ($;$) {
481             my $self = shift;
482             my $for_redraw = shift || 0;
483              
484             my $ply_strs = $self->{ply_strs};
485             my $ply_l = $self->{ply_l};
486             my $ply_e = $for_redraw ? $ply_l + 2 > @$ply_strs ? $ply_l + 1 : $ply_l + 2 : @$ply_strs;
487              
488             my $plies_to_show = $ply_e - $ply_l;
489             my $y = 20 * int(($ply_l - $self->{ply_o} + 2) / 2);
490             my $n = int($ply_l / 2) + 1;
491             my $show_1 = $ply_l % 2 == 0;
492             my $show_2 = $ply_e % 2 == 0;
493              
494             die "Internal: plies_to_show $plies_to_show is not in [1, 2]\n"
495             if $plies_to_show <= 0 || $plies_to_show > 2;
496             die "Internal: show_1 $show_1 + show_2 $show_2 != plies_to_show $plies_to_show\n"
497             if $show_1 + $show_2 != $plies_to_show;
498              
499             my $text = $self->{text};
500             my $display = $self->{display};
501             my $helper_x = $self->{helper_x};
502              
503             $text->write_xy($display, $helper_x, $y, $n)
504             if $show_1;
505             $text->write_xy($display, $helper_x + 30, $y, $ply_strs->[$ply_l++])
506             if $show_1;
507             $text->write_xy($display, $self->{helper_mid_x} + 15, $y, $ply_strs->[$ply_l++])
508             if $show_2;
509              
510             $self->{ply_l} = $ply_l;
511             }
512              
513             sub show_moves ($$) {
514             my $self = shift;
515             my $plies = shift;
516              
517             $self->clear_helper;
518              
519             $self->{ply_l} = $self->{ply_o};
520              
521             for (0 .. $self->{ply_m} / 2) {
522             last if $self->{ply_l} >= @{$self->{ply_strs}};
523             $self->show_last_move(1);
524             }
525             }
526              
527             sub show_move ($$$$$$) {
528             my $self = shift;
529             my $move = shift;
530             my $move_str = shift;
531             my $is_second = shift;
532             my $prev_plies = shift;
533              
534             push @{$self->{ply_strs}}, " " if !@$prev_plies && $is_second;
535             push @{$self->{ply_strs}}, $move_str;
536              
537             if ($is_second || $self->{ply_l} - $self->{ply_o} < $self->{ply_m}) {
538             $self->show_last_move;
539             } else {
540             $self->{ply_o} += 2;
541             $self->show_moves;
542             }
543              
544             $self->process_pending_events;
545             }
546              
547             sub show_result ($$) {
548             my $self = shift;
549             my $message = shift;
550              
551             my $text = $self->{text};
552             $text->h_align('center');
553             $text->color([220, 220, 150]);
554             $text->write_xy($self->{display}, $self->{helper_mid_x}, 0, $message);
555              
556             $self->process_pending_events;
557             }
558              
559             sub show_helper_buttons ($$) {
560             my $self = shift;
561             my @msgs = @_;
562              
563             $self->clear_helper;
564              
565             my $display = $self->{display};
566             my $y = 50;
567              
568             map {
569             my $text = SDLx::Text->new(
570             size => 18,
571             color => 0xffffdc,
572             shadow => 1,
573             x => $self->{helper_mid_x},
574             y => $y += 25,
575             h_align => 'center',
576             text => $_,
577             );
578             SDL::GFX::Primitives::filled_ellipse_RGBA($display, $self->{helper_mid_x}, $y + 11, $text->w / 2 + 10, 12, 0xF0, 0xE0, 0xF0, 55);
579             SDL::GFX::Primitives::ellipse_RGBA( $display, $self->{helper_mid_x}, $y + 11, $text->w / 2 + 10, 12, 0x00, 0x00, 0x00, 55);
580             $text->write_to($display);
581             [ $self->{helper_mid_x} - $text->w / 2, $y, $text->w, $text->h ]
582             } @msgs;
583             }
584              
585             sub edit_board ($;$) {
586             my $self = shift;
587             my $board = shift || $self->{board};
588              
589             $self->{board} = $board;
590              
591             my $orig_board = $board->clone;
592             my $display = $self->{display};
593              
594             my @rects = (
595             [ $self->{helper_mid_x} - 82, 264, 64, 64 ],
596             [ $self->{helper_mid_x} + 18, 264, 64, 64 ],
597             [ $self->{helper_mid_x} - 82, 364, 64, 64 ],
598             [ $self->{helper_mid_x} + 18, 364, 64, 64 ],
599             );
600             my @cp = (
601             [ White, Pawn ],
602             [ Black, Pawn ],
603             [ White, King ],
604             [ Black, King ],
605             );
606              
607             my $current = 0;
608              
609             push @rects, $self->show_helper_buttons(
610             "Finish editing (Enter)",
611             "Random board (a)",
612             "Empty board (e)",
613             "Reset board (r)",
614             "Reset or abort (Esc)",
615             );
616              
617             while (1) {
618             for my $i (0 .. 3) {
619             my $rect = $rects[$i];
620             if ($i == $current) {
621             SDL::Video::blit_surface(
622             $self->{bg}, SDL::Rect->new($self->{b_x}, $self->{b_y} + $::RULES{BOTTOM_LEFT_CELL} * 64, 64, 64),
623             $display, SDL::Rect->new(@$rect),
624             );
625             } else {
626             $self->blit_bg($rect);
627             }
628             my $piece_rect = SDL::Rect->new($rect->[0] + 8, $rect->[1] + 8, 48, 48);
629             my ($color, $piece) = @{$cp[$i]};
630             SDL::Video::blit_surface(
631             $self->{pieces}{$piece}{$color}, 0,
632             $display, $piece_rect,
633             );
634             }
635              
636             $self->show_board;
637             $self->show_title(sprintf "Edit Board (Balance: %+d)", $board->get_score);
638             my ($rv, $which, $is_second) = $self->wait_for_press(\@rects);
639              
640             if ($rv == RECT_PRESSED) {
641             if ($which < 4) {
642             $current = $which;
643             } elsif ($which == 7) {
644             $rv = RESTART_PRESSED;
645             } elsif ($which == 8) {
646             $rv = QUIT_PRESSED;
647             } else {
648             $rv = KEY_PRESSED;
649             $which = (SDLK_RETURN, SDLK_a, SDLK_e, 0)[$which - 4];
650             }
651             }
652             if ($rv == QUIT_PRESSED) {
653             last if $board->equals($orig_board);
654             $rv = RESTART_PRESSED;
655             }
656             if ($rv == RESTART_PRESSED) {
657             $board->copy($orig_board);
658             }
659             if ($rv == BOARD_LOC_PRESSED) {
660             my $loc = $which;
661             $is_second || $board->chk($loc, @{$cp[$current]})
662             ? $board->clr($loc)
663             : $board->set($loc, @{$cp[$current]});
664             }
665             if ($rv == KEY_PRESSED) {
666             my $key_sym = $which;
667             if ($key_sym == SDLK_RETURN || $key_sym == SDLK_KP_ENTER) {
668             last;
669             }
670             if ($key_sym == SDLK_e) {
671             $board->init("empty");
672             }
673             if ($key_sym == SDLK_a) {
674             $board->init("random");
675             }
676             }
677             }
678              
679             $self->clear_helper;
680              
681             return $board;
682             }
683              
684             sub select_menu ($$$) {
685             my $self = shift;
686             my $title = shift || "Please select";
687             my $items = shift || die "No items";
688             my $current = shift;
689             die "No array" unless ref($items) eq 'ARRAY';
690              
691             my @rects = $self->show_helper_buttons(@$items);
692             my ($rv, $which, $is_second) = $self->wait_for_press(\@rects, "Esc - Cancel");
693              
694             return $rv == RECT_PRESSED && $which < @$items
695             ? $items->[$which]
696             : undef;
697             }
698              
699             sub show_menu ($;$) {
700             my $self = shift;
701             my $board = shift || $self->{board};
702              
703             $self->{board} = $board;
704              
705             my $orig_board = $board->clone;
706             my %orig_RULES = %::RULES;
707              
708             while (1) {
709             $self->show_title("Welcome to Checkers");
710              
711             my $variant = $::RULES{variant};
712             my $size = $self->{board}->size;
713              
714             my @rects = $self->show_helper_buttons(
715             "Play Game (Enter)",
716             "Opponents [C vs C]",
717             "Variant [$variant]",
718             "Board Size [$size]",
719             "Edit Board Pieces (e)",
720             "Customize Rule Items (c)",
721             "Show Game Rules (h)",
722             "Restore Defaults (r)",
723             "Quit (Esc)",
724             );
725              
726             $self->show_board(1);
727             my ($rv, $which, $is_second) = $self->wait_for_press(\@rects);
728              
729             if ($rv == RECT_PRESSED) {
730             if ($which == 7) {
731             $rv = RESTART_PRESSED;
732             } elsif ($which == 8) {
733             $rv = QUIT_PRESSED;
734             } else {
735             $rv = KEY_PRESSED;
736             $which = (SDLK_RETURN, SDLK_o, SDLK_v, SDLK_s, SDLK_e, SDLK_c, SDLK_h)[$which];
737             }
738             }
739             if ($rv == QUIT_PRESSED) {
740             return;
741             }
742             if ($rv == RESTART_PRESSED) {
743             unless ($self->{board}->equals($orig_board) && join("\n", %::RULES) eq join("\n", %orig_RULES)) {
744             %::RULES = %orig_RULES;
745             $self->{board} = $orig_board->clone;
746             $self->init_video;
747             }
748             }
749             if ($rv == BOARD_LOC_PRESSED) {
750             $self->edit_board;
751             }
752             if ($rv == KEY_PRESSED) {
753             my $key_sym = $which;
754             if ($key_sym == SDLK_RETURN || $key_sym == SDLK_KP_ENTER) {
755             last;
756             }
757             if ($key_sym == SDLK_o) {
758             }
759             if ($key_sym == SDLK_v) {
760             my @variants = Games::Checkers::Rules::get_main_variants();
761             my $variant0 = $self->select_menu('Variant', \@variants, $variant);
762             if (defined $variant0 && $variant0 ne $variant) {
763             Games::Checkers::Rules::set_variant($variant = $variant0);
764             $self->{board} = Games::Checkers::Board->new;
765             $self->init_video;
766             }
767             }
768             if ($key_sym == SDLK_s || $key_sym == SDLK_b) {
769             my $size0 = $self->select_menu('Board Size', [qw(
770             4x4 6x6 8x8
771             8x10 10x8 10x10
772             12x12 14x14 16x16
773             )]);
774             if (defined $size0 && $size0 ne $size) {
775             $self->{board} = Games::Checkers::Board->new(undef, $size0);
776             $self->init_video;
777             }
778             }
779             if ($key_sym == SDLK_e) {
780             $self->edit_board;
781             }
782             if ($key_sym == SDLK_c) {
783             }
784             if ($key_sym == SDLK_h) {
785             }
786             }
787             }
788              
789             $self->clear_helper;
790              
791             return $self->{board};
792             }
793              
794             1;