5a8645280e22e06a22e853a46c202c1a22cd13e2
[wormy.git] / lib / Games / Wormy / Render.pm
1 package Games::Wormy::Render;
2
3 use 5.010;
4 use strict;
5 use warnings;
6 use experimental 'switch';
7
8 use Imager;
9 use List::Util qw(sum max);
10
11 our $VERSION = '1.00';
12
13 our %COL = (
14         bg     => Imager::Color->new(255, 255, 255),
15         wall   => Imager::Color->new(  0,   0,   0),
16         player => [
17                 {
18                         main  => Imager::Color->new(hue => 120, v => 1, s => .67),
19                         outer => Imager::Color->new(hue => 120, v => 1, s => .33),
20                 },
21         ],
22         bouncy => Imager::Color->new(hue => 180, v => 1, s => 1),
23 );
24
25 sub level {
26         my ($self, $level) = @_;
27
28         my $field = Imager->new(xsize => $level->{width}, ysize => $level->{height});
29         $field->box(filled => 1, color => $COL{bg});
30
31         # initial player paths
32         require Math::Trig;
33         my $DIRMULT = Math::Trig::pi() / 128;
34         for my $num (0 .. $#{ $level->{worms} }) {
35                 my $player = $level->{worms}->[$num];
36                 $field->line(
37                         x1 => $player->{x},
38                         y1 => $player->{y},
39                         x2 => $player->{x} + sin($_ * $DIRMULT) * 15,
40                         y2 => $player->{y} + cos($_ * $DIRMULT) * 15,
41                         color => $COL{player}->[$num]->{outer},
42                 ) for $player->{d} + 13, $player->{d} - 13;
43                 $field->line(
44                         x1 => $player->{x},
45                         y1 => $player->{y},
46                         x2 => $player->{x} + sin($player->{d} * $DIRMULT) * 20,
47                         y2 => $player->{y} + cos($player->{d} * $DIRMULT) * 20,
48                         color => $COL{player}->[$num]->{main},
49                 );
50                 $field->circle(
51                         x => $player->{x},
52                         y => $player->{y},
53                         r => 2,
54                         color => $COL{player}->[$num]->{main},
55                 );
56                 last;
57         }
58
59         # outer field borders
60         $field->box(
61                 xmin => $_,   ymin => 0,
62                 xmax => $_+1, ymax => $level->{height} - 1,
63                 filled => 1,
64                 color => $COL{wall},
65         ) for 0, $level->{width} - 2;
66         $field->box(
67                 ymin => $_,   xmin => 2,
68                 ymax => $_+1, xmax => $level->{width} - 1,
69                 filled => 1,
70                 color => $COL{wall},
71         ) for 0, $level->{height} - 2;
72
73         # draw objects
74         for my $object (@{ $level->{objects} }) {
75                 my @x = @$object{'x1', 'x2'};
76                 my @y = @$object{'y1', 'y2'};
77                 given ($object->{type}) {
78                         when (1) {
79                                 $field->line(
80                                         x1 => $x[0],
81                                         y1 => $y[0],
82                                         x2 => $x[1],
83                                         y2 => $y[1],
84                                         color => $COL{wall},
85                                 );
86                         }
87                         when (2) {
88                                 $field->polyline(
89                                         points => [
90                                                 [  $x[0], $y[0]], [  $x[1], $y[1]],
91                                                 [++$x[1], $y[1]], [++$x[0], $y[0]],
92                                                 [$x[0], ++$y[0]], [$x[1], ++$y[1]],
93                                                 [--$x[1], $y[1]], [--$x[0], $y[0]],
94                                         ],
95                                         color => $COL{wall},
96                                 );
97                         }
98                         when (3) {
99                                 $field->box(
100                                         xmin => $x[0],
101                                         ymin => $y[0],
102                                         xmax => $x[1],
103                                         ymax => $y[0] + $y[1],
104                                         filled => 1,
105                                         color => $COL{wall},
106                                 );
107                         }
108                         when (4) {
109                                 $field->circle(
110                                         x => $x[0],
111                                         y => $y[0],
112                                         r => $x[1],
113                                         filled => 1,
114                                         color => $COL{wall},
115                                 );
116                         }
117                 }
118         }
119
120         for my $bouncy (@{ $level->{balls} }) {
121                 $field->box(
122                         xmin => $bouncy->{x},
123                         ymin => $bouncy->{y},
124                         xmax => $bouncy->{x} + 1,
125                         ymax => $bouncy->{y} + 1,
126                         color => $COL{bouncy},
127                 );
128         }
129
130         return $field;
131 }
132
133 sub composite {
134         my $self = shift;
135
136         # single level can be returned directly
137         return $self->level($_[0]) if @_ == 1;
138
139         # concatenate images of multiple levels
140         my $width  = max map { $_->{width} } @_;
141         my $height = sum(map { $_->{height} + 1 } @_) - 1;
142         my $output = Imager->new(xsize => $width, ysize => $height);
143         $output->box(filled => 1, color => [64, 0, 0]);
144         $height = 0;
145         for (@_) {
146                 $output->paste(src => $self->level($_), top => $height);
147                 $height += $_->{height} + 1;
148         }
149         return $output;
150 }
151
152 1;
153