parse-wormedit: warn about missing level data for render
[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.01';
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         $level or return;
28
29         my $field = Imager->new(xsize => $level->{width}, ysize => $level->{height});
30         $field->box(filled => 1, color => $COL{bg});
31
32         # initial player paths
33         require Math::Trig;
34         my $DIRMULT = Math::Trig::pi() / 128;
35         for my $num (0 .. $#{ $level->{worms} }) {
36                 my $player = $level->{worms}->[$num];
37                 $field->line(
38                         x1 => $player->{x},
39                         y1 => $player->{y},
40                         x2 => $player->{x} + sin($_ * $DIRMULT) * 15,
41                         y2 => $player->{y} + cos($_ * $DIRMULT) * 15,
42                         color => $COL{player}->[$num]->{outer},
43                 ) for $player->{d} + 13, $player->{d} - 13;
44                 $field->line(
45                         x1 => $player->{x},
46                         y1 => $player->{y},
47                         x2 => $player->{x} + sin($player->{d} * $DIRMULT) * 20,
48                         y2 => $player->{y} + cos($player->{d} * $DIRMULT) * 20,
49                         color => $COL{player}->[$num]->{main},
50                 );
51                 $field->circle(
52                         x => $player->{x},
53                         y => $player->{y},
54                         r => 2,
55                         color => $COL{player}->[$num]->{main},
56                 );
57                 last;
58         }
59
60         # outer field borders
61         $field->box(
62                 xmin => $_,   ymin => 0,
63                 xmax => $_+1, ymax => $level->{height} - 1,
64                 filled => 1,
65                 color => $COL{wall},
66         ) for 0, $level->{width} - 2;
67         $field->box(
68                 ymin => $_,   xmin => 2,
69                 ymax => $_+1, xmax => $level->{width} - 1,
70                 filled => 1,
71                 color => $COL{wall},
72         ) for 0, $level->{height} - 2;
73
74         # draw objects
75         for my $object (@{ $level->{objects} }) {
76                 my @x = @$object{'x1', 'x2'};
77                 my @y = @$object{'y1', 'y2'};
78                 given ($object->{type}) {
79                         when (1) {
80                                 $field->line(
81                                         x1 => $x[0],
82                                         y1 => $y[0],
83                                         x2 => $x[1],
84                                         y2 => $y[1],
85                                         color => $COL{wall},
86                                 );
87                         }
88                         when (2) {
89                                 $field->polyline(
90                                         points => [
91                                                 [  $x[0], $y[0]], [  $x[1], $y[1]],
92                                                 [++$x[1], $y[1]], [++$x[0], $y[0]],
93                                                 [$x[0], ++$y[0]], [$x[1], ++$y[1]],
94                                                 [--$x[1], $y[1]], [--$x[0], $y[0]],
95                                         ],
96                                         color => $COL{wall},
97                                 );
98                         }
99                         when (3) {
100                                 $field->box(
101                                         xmin => $x[0],
102                                         ymin => $y[0],
103                                         xmax => $x[1],
104                                         ymax => $y[0] + $y[1],
105                                         filled => 1,
106                                         color => $COL{wall},
107                                 );
108                         }
109                         when (4) {
110                                 $field->circle(
111                                         x => $x[0],
112                                         y => $y[0],
113                                         r => $x[1],
114                                         filled => 1,
115                                         color => $COL{wall},
116                                 );
117                         }
118                 }
119         }
120
121         for my $bouncy (@{ $level->{balls} }) {
122                 $field->box(
123                         xmin => $bouncy->{x},
124                         ymin => $bouncy->{y},
125                         xmax => $bouncy->{x} + 1,
126                         ymax => $bouncy->{y} + 1,
127                         color => $COL{bouncy},
128                 );
129         }
130
131         return $field;
132 }
133
134 sub composite {
135         my $self = shift;
136
137         # single level can be returned directly
138         return $self->level($_[0]) if @_ == 1;
139
140         # concatenate images of multiple levels
141         my $width  = max map { $_->{width} } @_;
142         my $height = sum(map { $_->{height} + 1 } @_) - 1;
143         my $output = Imager->new(xsize => $width, ysize => $height);
144         $output->box(filled => 1, color => [64, 0, 0]);
145         $height = 0;
146         for (@_) {
147                 $output->paste(src => $self->level($_), top => $height);
148                 $height += $_->{height} + 1;
149         }
150         return $output;
151 }
152
153 1;
154