#!/usr/bin/perl -w use strict; use SDL; use SDL::App; use SDL::Event; use SDL::Surface; use SDL::Timer; use vars qw ( $App $Background @GameObjects $BallSurface $GuySurface $HarpoonSurface @BallDesc $ScreenHeight $ScreenWidth %Keys ); @BallDesc = ( { 'width' => 128, 'height' => 106, 'speedY' => 11, 'rect' => new SDL::Rect(-x => 0, -y => 12, -width => 128, -height => 106) }, { 'width' => 96, 'height' => 80, 'speedY' => 9, 'rect' => new SDL::Rect(-x => 128, -y => 8, -width => 96, -height => 80) }, { 'width' => 64, 'height' => 53, 'speedY' => 7, 'rect' => new SDL::Rect(-x => 0, -y => 134, -width => 64, -height => 53) }, { 'width' => 32, 'height' => 28, 'speedY' => 5.5, 'rect' => new SDL::Rect(-x => 224, -y => 2, -width => 32, -height => 28) }, { 'width' => 16, 'height' => 15, 'speedY' => 4.5, 'rect' => new SDL::Rect(-x => 232, -y => 42, -width => 16, -height => 15) }, ); $ScreenWidth = 800; $ScreenHeight = 600; package GameObject; sub new { my ($class) = @_; my $self = { 'rect' => new SDL::Rect( -x => 0, -y => 0, -width => 0, -height => 0 ), 'speedX' => 2, 'speedY' => 2, 'x' => 0, 'y' => 0, 'w' => 10, 'h' => 10, }; bless $self, $class; } sub Delete { my $self = shift; my ($i); for ($i = 0; $i < scalar @::GameObjects; ++$i) { if ($::GameObjects[$i] eq $self) { splice @::GameObjects, $i, 1; last; } } $self->Clear(); } sub Advance { my ($self) = @_; $self->{x} += $self->{speedX}; $self->{y} += $self->{speedY}; } sub Clear { my ($self) = @_; $::App->fill( $self->{rect}, new SDL::Color() ); } sub TransferRect { my ($self) = @_; $self->{rect}->x($self->{x}); $self->{rect}->y($self->{y}); $self->{rect}->width($self->{w}); $self->{rect}->height($self->{h}); } sub Draw { my ($self) = @_; $self->TransferRect(); $::App->fill( $self->{rect}, new SDL::Color(-r => 0x80) ); } package Ball; @Ball::ISA = qw(GameObject); sub new { my ($class, $generation, $x, $y) = @_; my ($self); $self = new GameObject; %{$self} = ( %{$self}, 'x' => $x, 'y' => $y, 'generation' => $generation, 'w' => $::BallDesc[$generation]->{width}, 'h' => $::BallDesc[$generation]->{height}, 'desc' => $::BallDesc[$generation], ); bless $self, $class; } sub Advance { my ($self) = @_; $self->{speedY} += 0.1; $self->SUPER::Advance(); if ($self->{y} > $::ScreenHeight - $self->{h}) { $self->{y} = $::ScreenHeight - $self->{h}; $self->{speedY} = -$::BallDesc[$self->{generation}]->{speedY}; } if ($self->{y} < 0) { $self->{y} = 0; $self->{speedY} = abs($self->{speedY}); } if ($self->{x} < 0) { $self->{x} = 0; $self->{speedX} = abs( $self->{speedX} ); } if ($self->{x} > $::ScreenWidth - $self->{w}) { $self->{x} = $::ScreenWidth - $self->{w}; $self->{speedX} = -abs( $self->{speedX} ); } } sub Draw { my ($self) = @_; $self->TransferRect(); $::BallSurface->blit( $self->{desc}->{rect}, $::App, $self->{rect} ); } package Harpoon; package Guy; @Guy::ISA = qw(GameObject); sub new { my ($class, $number) = @_; my ($self); $self = new GameObject; %{$self} = ( %{$self}, 'number' => $number, 'x' => 100 + $number * 100, 'y' => $::ScreenHeight - 64, 'w' => 64, 'h' => 64, 'delay' => 0, 'speedY' => 0, 'speedX' => 0, 'dir' => $number % 2, 'state' => 'idle', 'harpoons' => 0, ); bless $self, $class; } sub Fire { my ($self) = @_; if ($self->{harpoons} < 2) { ++$self->{harpoons}; push @::GameObjects, (new Harpoon($self)); $self->{state} = 'shoot'; $self->{delay} = 10; } } sub Advance { my ($self) = @_; if ($self->{delay} > 0) { --$self->{delay}; return; } $self->{speedX} = 0; $self->{state} = 'idle'; if ( $::Keys{::SDLK_UP} ) { $self->Fire(); $::Keys{::SDLK_UP} = 0; } elsif ( $::Keys{::SDLK_LEFT} ) { $self->{speedX} = -3; $self->{dir} = 0; $self->{state} = 'walk'; } elsif ( $::Keys{::SDLK_RIGHT} ) { $self->{speedX} = 3; $self->{dir} = 1; $self->{state} = 'walk'; } $self->SUPER::Advance(); if ($self->{x} < 0) { $self->{x} = 0; } if ($self->{x} > $::ScreenWidth - $self->{w}) { $self->{x} = $::ScreenWidth - $self->{w}; } } sub Draw { my ($self) = @_; my ($srcrect, $srcx, $srcy, $srcw, $srch); $srcw = $srch = 64; if ($self->{state} eq 'idle') { $srcx = $self->{dir} * 128; $srcy = 64; } elsif ($self->{state} eq 'walk') { $srcx = $self->{dir} * 256 + (int($self->{x} / 16) % 4) * 64; $srcy = 0; } elsif ($self->{state} eq 'shoot') { $srcx = $self->{dir} * 128; $srcx += 64 if ($self->{delay} >= 4); $srcy = 64; } $srcrect = new SDL::Rect( -width => $srcw, -height => $srch, -x => $srcx, -y => $srcy ); $self->TransferRect(); $::GuySurface->blit($srcrect, $::App, $self->{rect}); } package Harpoon; @Harpoon::ISA = qw(GameObject); sub new { my ($class, $guy) = @_; my ($self); $self = new GameObject; %{$self} = ( %{$self}, 'x' => $guy->{x} + 22, 'y' => $::ScreenHeight - 32, 'w' => 18, 'h' => 32, 'anim' => 0, 'speedY' => -5, 'speedX' => 0, 'guy' => $guy, ); bless $self, $class; } sub Delete { my $self = shift; --$self->{guy}->{harpoons}; $self->SUPER::Delete(); } sub Advance { my $self = shift; $self->{y} += $self->{speedY}; if ($self->{y} < 0) { $self->Delete(); return; } $self->{h} = $::ScreenHeight - $self->{y}; ++$self->{anim}; } sub Draw { my $self = shift; my ($x, $y, $h, $maxh, $dstrect, $srcrect); $self->TransferRect(); $y = $self->{y}; $dstrect = new SDL::Rect( -w => $self->{w}, -x => $self->{x}, -y => $y ); $srcrect = new SDL::Rect( -w => $self->{w}, -x => (40, 70, 102)[ int($self->{anim} / 4) % 3 ], -y => 0 ); $maxh = 64; while ($y < $::ScreenHeight) { $h = $::ScreenHeight - $y; $h = $maxh if $h > $maxh; $dstrect->y( $y ); $dstrect->height( $h ); $srcrect->height( $h ); $::HarpoonSurface->blit( $srcrect, $::App, $dstrect ); $y += $h; $srcrect->y( 32 ); $maxh = 32; } } package main; sub LoadSurfaces { my ($i); $BallSurface = new SDL::Surface( -name => "ball.png" ); $GuySurface = new SDL::Surface( -name => "guy.png" ); $HarpoonSurface = new SDL::Surface( -name => "harpoon.png" ); } sub AdvanceGame { foreach my $gameObject (@GameObjects) { $gameObject->Advance(); } } sub HandleEvents { my ($event, $type); $event = new SDL::Event; while (1) { last unless $event->poll(); $type = $event->type(); if ($type == SDL_QUIT) { exit; } elsif ($type == SDL_KEYDOWN) { my $keypressed = $event->key_sym; $Keys{$keypressed} = 1; if ($keypressed == SDLK_q) { exit; } } elsif ($type == SDL_KEYUP) { my $keypressed = $event->key_sym; $Keys{$keypressed} = 0; } } } sub DrawGame { my ($gameObject); foreach $gameObject (@GameObjects) { $gameObject->Clear(); } foreach $gameObject (@GameObjects) { $gameObject->Draw(); } $App->sync(); } $App = new SDL::App -title => "test.app", -width => $ScreenWidth, -height => $ScreenHeight, -depth => 32; # -fullscreen => 1; &LoadSurfaces(); my $guy = new Guy(0); push @GameObjects, ( $guy, new Ball(0, rand(600), 100), new Ball(1, rand(600), 100), new Ball(2, rand(600), 100), new Ball(3, rand(600), 100), new Ball(4, rand(600), 100), ); my $event = new SDL::Event; my ($lastTick, $currentTick, $advance); $currentTick = $App->ticks; $lastTick = $currentTick; while (1) { $currentTick = $App->ticks; $advance = int( ($currentTick - $lastTick ) / 10 ); if ($advance > 10) { print STDERR "advance = $advance!\n"; $advance = 10; } if ($advance <= 0) { $App->delay(10); next; } while ($advance--) { &AdvanceGame(); } $lastTick = $currentTick; &DrawGame(); &HandleEvents(); }