parse-wormedit: level rendering
[wormy.git] / lib / Games / Wormy / Render.pm
diff --git a/lib/Games/Wormy/Render.pm b/lib/Games/Wormy/Render.pm
new file mode 100644 (file)
index 0000000..a1f29f5
--- /dev/null
@@ -0,0 +1,152 @@
+package Games::Wormy::Render;
+
+use 5.010;
+use strict;
+use warnings;
+
+use Imager;
+use List::Util qw(sum max);
+
+our $VERSION = '1.00';
+
+our %COL = (
+       bg     => Imager::Color->new(255, 255, 255),
+       wall   => Imager::Color->new(  0,   0,   0),
+       player => [
+               {
+                       main  => Imager::Color->new(hue => 120, v => 1, s => .67),
+                       outer => Imager::Color->new(hue => 120, v => 1, s => .33),
+               },
+       ],
+       bouncy => Imager::Color->new(hue => 180, v => 1, s => 1),
+);
+
+sub level {
+       my ($self, $level) = @_;
+
+       my $field = Imager->new(xsize => $level->{width}, ysize => $level->{height});
+       $field->box(filled => 1, color => $COL{bg});
+
+       # initial player paths
+       require Math::Trig;
+       my $DIRMULT = Math::Trig::pi() / 128;
+       for my $num (0 .. $#{ $level->{worms} }) {
+               my $player = $level->{worms}->[$num];
+               $field->line(
+                       x1 => $player->{x},
+                       y1 => $player->{y},
+                       x2 => $player->{x} + sin($_ * $DIRMULT) * 15,
+                       y2 => $player->{y} + cos($_ * $DIRMULT) * 15,
+                       color => $COL{player}->[$num]->{outer},
+               ) for $player->{d} + 13, $player->{d} - 13;
+               $field->line(
+                       x1 => $player->{x},
+                       y1 => $player->{y},
+                       x2 => $player->{x} + sin($player->{d} * $DIRMULT) * 20,
+                       y2 => $player->{y} + cos($player->{d} * $DIRMULT) * 20,
+                       color => $COL{player}->[$num]->{main},
+               );
+               $field->circle(
+                       x => $player->{x},
+                       y => $player->{y},
+                       r => 2,
+                       color => $COL{player}->[$num]->{main},
+               );
+               last;
+       }
+
+       # outer field borders
+       $field->box(
+               xmin => $_,   ymin => 0,
+               xmax => $_+1, ymax => $level->{height} - 1,
+               filled => 1,
+               color => $COL{wall},
+       ) for 0, $level->{width} - 2;
+       $field->box(
+               ymin => $_,   xmin => 2,
+               ymax => $_+1, xmax => $level->{width} - 1,
+               filled => 1,
+               color => $COL{wall},
+       ) for 0, $level->{height} - 2;
+
+       # draw objects
+       for my $object (@{ $level->{objects} }) {
+               my @x = @$object{'x1', 'x2'};
+               my @y = @$object{'y1', 'y2'};
+               given ($object->{type}) {
+                       when (1) {
+                               $field->line(
+                                       x1 => $x[0],
+                                       y1 => $y[0],
+                                       x2 => $x[1],
+                                       y2 => $y[1],
+                                       color => $COL{wall},
+                               );
+                       }
+                       when (2) {
+                               $field->polyline(
+                                       points => [
+                                               [  $x[0], $y[0]], [  $x[1], $y[1]],
+                                               [++$x[1], $y[1]], [++$x[0], $y[0]],
+                                               [$x[0], ++$y[0]], [$x[1], ++$y[1]],
+                                               [--$x[1], $y[1]], [--$x[0], $y[0]],
+                                       ],
+                                       color => $COL{wall},
+                               );
+                       }
+                       when (3) {
+                               $field->box(
+                                       xmin => $x[0],
+                                       ymin => $y[0],
+                                       xmax => $x[1],
+                                       ymax => $y[0] + $y[1],
+                                       filled => 1,
+                                       color => $COL{wall},
+                               );
+                       }
+                       when (4) {
+                               $field->circle(
+                                       x => $x[0],
+                                       y => $y[0],
+                                       r => $x[1],
+                                       filled => 1,
+                                       color => $COL{wall},
+                               );
+                       }
+               }
+       }
+
+       for my $bouncy (@{ $level->{balls} }) {
+               $field->box(
+                       xmin => $bouncy->{x},
+                       ymin => $bouncy->{y},
+                       xmax => $bouncy->{x} + 1,
+                       ymax => $bouncy->{y} + 1,
+                       color => $COL{bouncy},
+               );
+       }
+
+       return $field;
+}
+
+sub composite {
+       my $self = shift;
+
+       # single level can be returned directly
+       return $self->level($_[0]) if @_ == 1;
+
+       # concatenate images of multiple levels
+       my $width  = max map { $_->{width} } @_;
+       my $height = sum(map { $_->{height} + 1 } @_) - 1;
+       my $output = Imager->new(xsize => $width, ysize => $height);
+       $output->box(filled => 1, color => [64, 0, 0]);
+       $height = 0;
+       for (@_) {
+               $output->paste(src => $self->level($_), top => $height);
+               $height += $_->{height} + 1;
+       }
+       return $output;
+}
+
+1;
+