#!/usr/bin/perl
use 5.10.0;
use strict;
use warnings;
use SDL;
use SDL::Time;
use SDL::Rect;
use SDL::Event;
use SDL::Events;
use SDLx::Sprite::Animated;
use SDLx::Surface;
use SDLx::Controller;
use File::Spec::Functions;
use FindBin;
use lib catfile($FindBin::Bin,'..','lib');
use Games::Zumbis;
use Games::Zumbis::Mapa;
use Games::Zumbis::Tiro;
use Games::Zumbis::Zumbi;
use Games::Zumbis::TelaGameOver;
use Games::Zumbis::Audio;
use Getopt::Long;
use Pod::Usage;
my $help = 0;
my $fullscreen = 0;
GetOptions( 'fullscreen|fs=s' => \$fullscreen,
'help|?' => \$help,
) or pod2usage(1);
pod2usage(2) if $help;
my $mapa = Games::Zumbis::Mapa->new( arquivo => Games::Zumbis->sharedir->file('mapas/mapa-de-teste-1.xml') );
my $initial_ticks;
my $score = 0;
my $heroi = SDLx::Sprite::Animated->new(
image => Games::Zumbis->sharedir->file('dados/heroi.png'),
rect => SDL::Rect->new(5,14,32,45),
ticks_per_frame => 2,
);
my $heroina = SDLx::Sprite::Animated->new(
image => Games::Zumbis->sharedir->file('dados/heroina.png'),
rect => SDL::Rect->new(5,14,31,45),
ticks_per_frame => 2,
);
my $player = $heroina;
my $telagameover;
my $jogo;
my @zumbis;
my @morrendo;
my @tiros;
my $sequences = {
stop_left => [ [1, 3] ],
stop_right => [ [1, 1] ],
stop_up => [ [1, 0] ],
stop_down => [ [1, 2] ],
left => [ [0,3], [1,3], [2,3] ],
right => [ [0,1], [1,1], [2,1] ],
up => [ [0,0], [1,0], [2,0] ],
down => [ [0,2], [1,2], [2,2] ],
};
$heroi->set_sequences(%$sequences);
$heroina->set_sequences(%$sequences);
my ( $player_x, $player_y ) = $mapa->playerstart_px;
#$player->x( $player_x );
#$player->y( $player_y );
my $player_vel = 0.45;
$player->sequence('stop_down');
$player->start;
my $flags = SDL_HWSURFACE | SDL_DOUBLEBUF;
$flags = $flags | SDL_FULLSCREEN if $fullscreen;
my $tela = SDLx::Surface::display(
width => $mapa->width_px,
height => $mapa->height_px,
flags => $flags,
);
Games::Zumbis::Audio->init;
Games::Zumbis::Audio->start_music( Games::Zumbis->sharedir->file('dados/terrortrack.ogg'));
my %pressed;
sub eventos {
my $e = shift;
my $jogo = shift;
$jogo->stop if $e->type == SDL_QUIT;
$jogo->stop if $e->key_sym == SDLK_ESCAPE;
if ( $e->type == SDL_KEYDOWN ) {
my $tecla = $e->key_sym;
if ($tecla == SDLK_LEFT) {
$pressed{left} = 1;
}
elsif ($tecla == SDLK_RIGHT) {
$pressed{right} = 1;
}
elsif ($tecla == SDLK_DOWN) {
$pressed{down} = 1;
}
elsif($tecla == SDLK_UP) {
$pressed{up} = 1;
}
elsif ($tecla == SDLK_SPACE && scalar @tiros < 4) {
my $type = $player->sequence;
$type =~ s/stop_//;
push @tiros, Games::Zumbis::Tiro->new(x => $player_x, y => $player_y+20,
type => $type);
}
if (%pressed) {
$player->sequence((keys %pressed)[0]);
}
}
elsif ( $e->type == SDL_KEYUP ) {
my $tecla = $e->key_sym;
if ($tecla == SDLK_f) {
SDL::Video::wm_toggle_fullscreen( SDL::Video::get_video_surface() );
$fullscreen = not $fullscreen;
}
if ($tecla == SDLK_LEFT) {
delete $pressed{left};
$player->sequence('stop_left') unless %pressed;
}
elsif ($tecla == SDLK_RIGHT) {
delete $pressed{right};
$player->sequence('stop_right') unless %pressed;;
}
elsif ($tecla == SDLK_DOWN) {
delete $pressed{down};
$player->sequence('stop_down') unless %pressed;
}
elsif ($tecla == SDLK_UP) {
delete $pressed{up};
$player->sequence('stop_up') unless %pressed;
}
if (%pressed) {
$player->sequence((keys %pressed)[0]);
}
}
return 1;
}
my $last_zumbi_dt = 0;
sub cria_zumbis {
my $dt = shift;
$last_zumbi_dt += $dt;
if ($last_zumbi_dt > 400 && scalar @zumbis < 7) {
my ($x, $y) = $mapa->next_spawnpoint_px;
push @zumbis, Games::Zumbis::Zumbi->new(x => $x, y => $y);
$last_zumbi_dt = 0;
}
}
sub move_heroi {
my $dt = shift;
my $tilesize = $mapa->dados->{tilesize};
# verifica se o heroi foi tocado por um zumbi
# (condicao de derrota)
for my $z (@zumbis) {
next if abs($player_x - $z->x) > 25;
next if abs($player_y - $z->y) > 25;
init_game_over();
}
@tiros = grep { $_->tick($dt, $mapa) } @tiros;
@zumbis = grep { my $z = $_;
!grep {
my $t = $_;
((!$t->collided &&
(($player->sequence =~ 'up'
|| $player->sequence =~ 'down')?
(abs($t->{x} - 10 - $z->{x})<20 &&
abs($t->{y} - $z->{y})<20):
(abs($t->{x} - $z->{x})<20 &&
abs($t->{y} - 25 - $z->{y})<25)))?
($z->sequence($z->sequence!~/morrendo/?'morrendo_'.$z->sequence:()),
push(@morrendo,$z),
$score++,
$t->collided(1)
):0);
} @tiros
} @zumbis;
my $sequencia = $player->sequence;
my ($change_x, $change_y) = (0,0);
$change_x = 0 - $player_vel * $dt if $sequencia eq 'left';
$change_x = $player_vel * $dt if $sequencia eq 'right';
$change_y = 0 - $player_vel * $dt if $sequencia eq 'up';
$change_y = $player_vel * $dt if $sequencia eq 'down';
my $start_tilex = ($player_x + $change_x) / $tilesize;
my $start_tiley = ($player_y + $change_y) / $tilesize;
my $end_tilex = ($player_x + 32 + $change_x) / $tilesize;
my $end_tiley = ($player_y + 32 + $change_y) / $tilesize;
if (!$mapa->colisao->[$start_tilex][$start_tiley] &&
!$mapa->colisao->[$start_tilex][$end_tiley] &&
!$mapa->colisao->[$end_tilex][$start_tiley] &&
!$mapa->colisao->[$end_tilex][$end_tiley]) {
$player_x += $change_x;
$player_y += $change_y;
}
}
sub move_zumbis { $_->tick($_[0], $mapa, $player_x, $player_y) for @zumbis }
sub exibicao {
my $result = (SDL::get_ticks() - $initial_ticks )/1000;
$mapa->render( $tela->surface, $result, $score );
$_->render($tela->surface) for @morrendo;
$_->render($tela->surface) for @tiros;
$_->render($tela->surface) for @zumbis;
$player->draw_xy( $tela->surface, $player_x, $player_y );
# $tela->update;
$tela->flip;
return $result;
}
sub eventos_gameover {
my $e = shift;
$jogo->stop if $e->type == SDL_QUIT;
$jogo->stop if $e->key_sym == SDLK_ESCAPE;
if ( $e->type == SDL_KEYDOWN ) {
my $tecla = $e->key_sym;
if ($tecla == SDLK_RETURN || $tecla == SDLK_1 || $tecla == SDLK_2) {
if ($tecla == SDLK_1) {
$player = $heroi;
} elsif ($tecla == SDLK_2) {
$player = $heroina;
}
@zumbis = ();
@morrendo = ();
@tiros = ();
( $player_x, $player_y ) = $mapa->playerstart_px;
$player->sequence('stop_down');
init_game();
}
}
return 1;
}
sub render_gameover {
$telagameover->render($tela);
$tela->update;
}
sub init_game {
$jogo->remove_all_handlers;
$initial_ticks = SDL::get_ticks;
$score = 0;
$jogo->add_event_handler( \&eventos );
$jogo->add_show_handler( \&exibicao );
$jogo->add_move_handler( \&move_heroi );
$jogo->add_move_handler( \&cria_zumbis );
$jogo->add_move_handler( \&move_zumbis );
}
sub init_game_over {
%pressed = ();
$jogo->remove_all_handlers;
my $result = exibicao();
$telagameover = Games::Zumbis::TelaGameOver->new(surface => $tela,
tempo => $result,
score => $score );
$tela->update();
$jogo->add_event_handler( \&eventos_gameover );
#$jogo->add_move_handler( \&animar_gameover );
$jogo->add_show_handler( \&render_gameover );
}
$jogo = SDLx::Controller->new(dt => 0.003);
init_game();
$jogo->run;
# end of game, restore screen (if set to full)
SDL::Video::wm_toggle_fullscreen( SDL::Video::get_video_surface() ) if $fullscreen;
__END__
=head1 NAME
zumbis - a fast-paced zombie apocalypse arcade game
=head1 SYNOPSIS
zumbis [options]
Available options:
--fullscreen, --fs enable full screen mode
=head2 CONTROLS
Use the arrow keys to control the character, and the spacebar to shoot;
While in-game, you can press the 'f' key to toggle fullscreen mode, or
the [escape] key to exit the game;
Before restarting the game, press '1' to use a male character, or '2'
for the female character. Pressing the [enter] key will restart the
game with whatever character you've played before.
For more information, please type 'perldoc Games::Zumbis' on the command
line.
Thanks for playing! And watch out for those flesh eating things! :)